sed 例項講解 上篇

2021-04-16 01:53:21 字數 4844 閱讀 9692

前面的幾句羅嗦話

在 unix/linux 世界中有很多文字編輯器(如:vi、emacs 和 jed 以及很多其它工具)可供我們選擇。我們都有自己熟悉並且喜愛的編輯器(和我們喜愛的組合鍵)。有了可信賴的編輯器,我們可以輕鬆處理任何數量與 unix 有關的管理或程式設計任務。

雖然互動式編輯器很棒,但卻有其限制。儘管其互動式特性可以成為強項,但也有其不足之處。考慮一下需要對一組檔案執行類似更改的情形。您可能會本能地執行自己所喜愛的編輯器,然後手工執行一組煩瑣、重複和耗時的編輯任務。然而,有一種更好的方法。

開始說sed了,注意好好聽

如果可以使編輯檔案的過程自動化,以便用「批處理」方式編輯檔案,甚至編寫可以對現有檔案進行複雜更改的指令碼,那將太好了。幸運的是,對於這種情況,有一種更好的方法 -- 這種更好的方法稱為 "sed"。

sed 是一種幾乎包括在所有 unix/linux 平台的輕量級流編輯器。sed 有許多很好的特性。首先,它相當小巧,通常要比您所喜愛的指令碼語言小很多倍。其次,因為 sed 是一種流編輯器,所以,它可以對從如管道這樣的標準輸入接收的資料進行編輯。因此,無需將要編輯的資料儲存在磁碟上的檔案中。因為可以輕易將資料管道輸出 到 sed,所以,將 sed 用作強大的shell指令碼中長而複雜的管道很容易。試一下用您所喜愛的編輯器去那樣做。

gnu sed

對 linux 使用者來說幸運的是,最好的 sed 版本之一恰好是 gnu sed,其當前版本是 3.02。每乙個 linux 發行版都有(或至少應該有)gnu sed。gnu sed 之所以流行不僅因為可以自由分發其源**,還因為它恰巧有許多對 posix sed 標準便利、省時的擴充套件。另外,gnu 沒有 sed 早期專門版本的很多限制,如行長度限制 -- gnu 可以輕鬆處理任意長度的行。

、編譯然後安裝,而幾分鐘後我發現最新的 sed 版本卻是 3.02.80 -- 可在alpha.gnu.org 上 3.02a 源**旁邊找到其源**。安裝完 gnu sed 3.02.80 之後,我就完全準備好了。

alpha.gnu.org

alpha.gnu.org是新的和實驗性 gnu 源**的所在地。然而,您還會在那裡發現許多優秀、穩定的源**。出於某種原因,不是許多 gnu 開發人員忘記將穩定的源**移至ftp.gnu.org,就是它們的 "beta" 期間格外長(2 年!)。例如,sed 3.02a 已有兩年,甚至 3.02.80 也有一年。

正確的 sed

在 本系列中,將使用 gnu sed 3.02.80。在即將出現的本系列後續文章中,某些(但非常少)最高端的示例將不能在 gnu sed 3.02 或 3.02a 中使用。如果您使用的不是 gnu sed,那麼結果可能會不同。現在為什麼不花些時間安裝 gnu sed 3.02.80 呢?那樣,不僅可以為本系列的餘下部分作好準備,而且還可以使用可能是目前最好的 sed。

sed 示例

sed 通過對輸入資料執行任意數量使用者指定的編輯操作(「命令」)來工作。sed 是基於行的,因此按順序對每一行執行命令。然後,sed 將其結果寫入標準輸出 (stdout),它不修改任何輸入檔案。

讓我們看一些示例。頭幾個會有些奇怪,因為我要用它們演示 sed 如何工作,而不是執行任何有用的任務。然而,如果您是 sed 新手,那麼理解它們是十分重要的。下面是第乙個示例:

$ sed -e 'd' /etc/services

如果輸入該命令,將得不到任何輸出。那麼,發生了什麼?在該例中,用乙個編輯命令 'd' 呼叫 sed。sed 開啟 /etc/services 檔案,將一行讀入其模式緩衝區,執行編輯命令(「刪除行」),然後列印模式緩衝區(緩衝區已為空)。然後,它對後面的每一行重複這些步驟。這不會產生輸 出,因為 "d" 命令除去了模式緩衝區中的每一行!

在該例中,還有幾件事要注意。首先,根本沒有修改 /etc/services。這還是因為 sed 只讀取在命令列指定的檔案,將其用作輸入 -- 它不試圖修改該檔案。第二件要注意的事是 sed 是面向行的。'd' 命令不是簡單地告訴 sed 一下子刪除所有輸入資料。相反,sed 逐行將 /etc/services 的每一行讀入其稱為模式緩衝區的內部緩衝區。一旦將一行讀入模式緩衝區,它就執行 'd' 命令,然後列印模式緩衝區的內容(在本例中沒有內容)。我將在後面為您演示如何使用位址範圍來控制將命令應用到哪些行 -- 但是,如果不使用位址,命令將應用到所有行。

第三件要注意的事是括起 'd' 命令的單引號的用法。養成使用單引號來括起 sed 命令的習慣是個好注意,這樣可以禁用 shell 擴充套件。

另乙個 sed 示例

下面是使用 sed 從輸出流除去 /etc/services 檔案第一行的示例:

$ sed -e '1d' /etc/services | more

如您所見,除了前面有 '1' 之外,該命令與第乙個 'd' 命令十分類似。如果您猜到 '1' 指的是第一行,那您就猜對了。與第乙個示例中只使用 'd' 不同的是,這一次使用的 'd' 前面有乙個可選的數字位址。通過使用位址,可以告訴 sed 只對某一或某些特定行進行編輯。

$ sed -e '1,10d' /etc/services | more

當用逗號將兩個位址分開時,sed 將把後面的命令應用到從第乙個位址開始、到第二個位址結束的範圍。在本例中,將 'd' 命令應用到第 1 到 10 行(包括這兩行)。所有其它行都被忽略。

帶規則表示式的位址

現在演示乙個更有用的示例。假設要檢視 /etc/services 檔案的內容,但是對檢視其中包括的注釋部分不感興趣。如您所知,可以通過以 '#' 字元開頭的行在 /etc/services 檔案中放置注釋。為了避免注釋,我們希望 sed 刪除以 '#' 開始的行。以下是具體做法:

$ sed -e '/^#/d' /etc/services | more

試一下該例,看看發生了什麼。您將注意到,sed 成功完成了預期任務。現在,讓我們分析發生的情況。

要理解 '/^#/d' 命令,首先需要對其剖析。首先,讓我們除去 'd' -- 這是我們前面所使用的同乙個刪除行命令。新增加的是 '/^#/' 部分,它是一種新的規則表示式位址。規則表示式位址總是由斜槓括起。它們指定一種 模式,緊跟在規則表示式位址之後的命令將僅適用於正好與該特定模式匹配的行。

因此,'/^#/' 是乙個規則表示式。但是,它做些什麼呢?很明顯,現在該複習規則表示式了。

規則表示式複習

可以使用規則表示式來表示可能會在文字中發現的模式。您在 shell 命令列中用過 '*' 字元嗎?這種用法與規則表示式類似,但並不相同。下面是可以在規則表示式中使用的特殊字元:

字元  描述 

%  與行首匹配 

與行末尾匹配 

與任乙個字元匹配 

將與前乙個字元的零或多個出現匹配 

[ ]  與 [ ] 之內的所有字元匹配 

規則表示式  描述 

/./  將與包含至少乙個字元的任何行匹配 

/../  將與包含至少兩個字元的任何行匹配 

/^#/  將與以 '#' 開始的任何行匹配 

/^$/  將與所有空行匹配 

/}^/  將與以 '}'(無空格)結束的任何行匹配 

/} *^/  將與以 '}' 後面跟有零或多個空格結束的任何行匹配 

/[abc]/ 將與包含小寫 'a'、'b' 或 'c' 的任何行匹配 

/^[abc]/ 將與以 'a'、'b' 或 'c'開始的任何行匹配 

在這些示例中,鼓勵您嘗試幾個。花一些時間熟悉規則表示式,然後嘗試幾個自己建立的規則表示式。可以如下使用 regexp:

$ sed -e '/regexp/d' /path/to/my/test/file | more

這將導致 sed 刪除任何匹配的行。然而,通過告訴 sed列印 regexp 匹配並刪除不匹配的內容,而不是與之相反的方法,會更有利於熟悉規則表示式。可以用以下命令這樣做:

$ sed -n -e '/regexp/p' /path/to/my/test/file | more

請注意新的 '-n' 選項,該選項告訴 sed 除非明確要求列印模式空間,否則不這樣做。您還會注意到,我們用 'p' 命令替換了 'd' 命令,如您所猜想的那樣,這明確要求 sed 列印模式空間。就這樣,將只列印匹配部分。

$ sed -n -e '/begin/,/end/p' /my/test/file | more

如果沒發現 "begin",那麼將不列印資料。如果發現了 "begin",但是在這之後的所有行中都沒發現 "end",那麼將列印所有後續行。發生這種情況是因為 sed 面向流的特性 -- 它不知道是否會出現 "end"。

c 源**示例

如果只要列印 c 原始檔中的 main() 函式,可輸入:

$ sed -n -e '/main*(/,/^}/p' sourcefile.c | more

該命令有兩個規則表示式 '/main*(/' 和 '/^}/',以及乙個命令 'p'。第乙個規則表示式將與後面依次跟有任意數量的空格或製表鍵以及開始圓括號的字串 "main" 匹配。這應該與一般 ansi c main() 宣告的開始匹配。

在這個特別的規則表示式中,出現了 '' 字元類。這只是乙個特殊的關鍵字,它告訴 sed 與 tab 或空格匹配。如果願意的話,可以不輸入 '',而輸入 '[',然後是空格字母,然後是 -v,然後再輸入製表鍵字母和 ']' -- control-v 告訴bash要插入「真正」的製表鍵,而不是執行命令擴充套件。使用 '' 命令類(特別是在指令碼中)會更清楚。

好,現在看一下第二個 regexp。'/^}' 將與任何出現在新行行首的 '}' 字元匹配。如果**的格式很好,那麼這將與 main() 函式的結束花括號匹配。如果格式不好,則不會正確匹配 -- 這是執行模式匹配任務的一件棘手之事。

因為是處於 '-n' 安靜方式,所以 'p' 命令還是完成其慣有任務,即明確告訴 sed 列印該行。試著對 c 原始檔執行該命令 -- 它應該輸出整個 main() 塊,包括開始的 "main()" 和結束的 '}'。 

例項講解3 sed命令a

sed的正規表示式用 括住。1 sed文字的定位方法 xx為一行號 x,y表示行號範圍從x到y pattern 查詢包含模式的行 pattern pattern 查詢包含兩種模式的行 pattern x 在給定行號上查詢包含模式的行 x,pattern 通過行號和模式查詢匹配行 x,y 查詢不包括指...

例項講解3 sed命令b

3 sed列印檔案的第二行 sed n 2p filename 4 sed列印檔案的第一到三行 sed n 1,2p filename 5 sed列印匹配test的行 sed n test p filename 6 sed列印匹配 的行 sed n p filename 7 sed列印最後一行 是代...

例項講解3 sed命令d

18 sed常見的一行命令集 命令意思 s g 刪除以句點為結尾行 e abcd d 刪除包含abcd的行 疑為 abcd d s g 刪除乙個以上空格,用乙個空格代替 s g 刪除行首空格 s g 刪除句點後跟兩個或多個空格,用乙個空格代替 s d 刪除空行 sh不支援d,但在ksh下支援 s g...