sed 編輯器是 Linux 系統管理員的工具包中最有用的資產之一,因此,有必要徹底地了解其應用
Linux 操作系統最大的一個好處是它帶有各種各樣的實用工具。存在如此之多不同的實用工具,幾乎不可能知道並了解所有這些工具。可以簡化關鍵情況下操作的一個實用 工具是 sed。它是任何管理員的工具包中最強大的工具之一,並且可以證明它自己在關鍵情況下非常有價值。
sed 實用工具是一個“編輯器”,但它與其它大多數編輯器不同。除了不面向屏幕之外,它還是非交互式的。這意味著您必須將要對數據執行的命令插入到命令行或要處 理的腳本中。當顯示它時,請忘記您在使用 Microsoft Word 或其它大多數編輯器時擁有的交互式編輯文件功能。sed 在一個文件(或文件集)中非交互式、並且不加詢問地接收一系列的命令並執行它們。因而,它流經文本就如同水流經溪流一樣,因而 sed 恰當地代表了流編輯器。它可以用來將所有出現的 "Mr. Smyth" 修改為 "Mr. Smith",或將 "tiger cub" 修改為 "wolf cub"。流編輯器非常適合於執行重復的編輯,這種重復編輯如果由人工完成將花費大量的時間。其參數可能和一次性使用一個簡單的操作所需的參數一樣有限, 或者和一個具有成千上萬行要進行編輯修改的腳本文件一樣復雜。sed 是 Linux 和 UNIX 工具箱中最有用的工具之一,且使用的參數非常少。
sed 的工作方式
sed 實用工具按順序逐行將文件讀入到內存中。然後,它執行為該行指定的所有操作,並在完成請求的修改之後將該行放回到內存中,以將其轉儲至終端。完成了這一行 上的所有操作之後,它讀取文件的下一行,然後重復該過程直到它完成該文件。如同前面所提到的,默認輸出是將每一行的內容輸出到屏幕上。在這裡,開始涉及到 兩個重要的因素—首先,輸出可以被重定向到另一文件中,以保存變化;第二,源文件(默認地)保持不被修改。sed 默認讀取整個文件並對其中的每一行進行修改。不過,可以按需要將操作限制在指定的行上。
該實用工具的語法為:
sed [options] \'{command}\' [filename]
在這篇文章中,我們將浏覽最常用的命令和選項,並演示它們如何工作,以及它們適於在何處使用。
替換命令
sed 實用工具以及其它任何類似的編輯器的最常用的命令之一是用一個值替換另一個值。用來實現這一目的的操作的命令部分語法是:
\'s/{old value}/{new value}/\'
因而,下面演示了如何非常簡單地將 "tiger" 修改為 "wolf":
$ echo The tiger cubs will meet on Tuesday after school | sed
\'s/tiger/wolf/\'
The wolf cubs will meet on Tuesday after school
$
注意如果輸入是源自之前的命令輸出,則不需要指定文件名—同樣的原則也適用於 awk、sort 和其它大多數 LinuxUNIX 命令行實用工具程序。
多次修改
如果需要對同一文件或行作多次修改,可以有三種方法來實現它。第一種是使用 "-e" 選項,它通知程序使用了多條編輯命令。例如:
$ echo The tiger cubs will meet on Tuesday after school | sed -e \'
s/tiger/wolf/\' -e \'s/after/before/\'
The wolf cubs will meet on Tuesday before school
$
這是實現它的非常復雜的方法,因此 "-e" 選項不常被大范圍使用。更好的方法是用分號來分隔命令:
$ echo The tiger cubs will meet on Tuesday after school | sed \'
s/tiger/wolf/; s/after/before/\'
The wolf cubs will meet on Tuesday before school
$
注 意分號必須是緊跟斜線之後的下一個字符。如果兩者之間有一個空格,操作將不能成功完成,並返回一條錯誤消息。這兩種方法都很好,但許多管理員更喜歡另一種 方法。要注意的一個關鍵問題是,兩個撇號 (\' \') 之間的全部內容都被解釋為 sed 命令。直到您輸入了第二個撇號,讀入這些命令的 shell 程序才會認為您完成了輸入。這意味著可以在多行上輸入命令—同時 Linux 將提示符從 PS1 變為一個延續提示符(通常為 ">")—直到輸入了第二個撇號。一旦輸入了第二個撇號,並且按下了 Enter 鍵,則處理就進行並產生相同的結果,如下所示:
$ echo The tiger cubs will meet on Tuesday after school | sed \'
> s/tiger/wolf/
> s/after/before/\'
The wolf cubs will meet on Tuesday before school
$
全局修改
讓我們開始一次看似簡單的編輯。假定在要修改的消息中出現了多次要修改的項目。默認方式下,結果可能和預期的有所不同,如下所示:
$ echo The tiger cubs will meet this Tuesday at the same time
as the meeting last Tuesday | sed \'s/Tuesday/Thursday/\'
The tiger cubs will meet this Thursday at the same time
as the meeting last Tuesday
$
與 將出現的每個 "Tuesday" 修改為 "Thursday" 相反,sed 編輯器在找到一個要修改的項目並作了修改之後繼續處理下一行,而不讀整行。sed 命令功能大體上類似於替換命令,這意味著它們都處理每一行中出現的第一個選定序列。為了替換出現的每一個項目,在同一行中出現多個要替換的項目的情況下, 您必須指定在全局進行該操作:
$ echo The tiger cubs will meet this Tuesday at the same time
as the meeting last Tuesday | sed \'s/Tuesday/Thursday/g\'
The tiger cubs will meet this Thursday at the same time
as the meeting last Thursday
$
請記住不管您要查找的序列是否僅包含一個字符或詞組,這種對全局化的要求都是必需的。
sed 還可以用來修改記錄字段分隔符。例如,以下命令將把所有的 tab 修改為空格:
sed \'s// /g\'
其 中,第一組斜線之間的項目是一個 tab,而第二組斜線之間的項目是一個空格。作為一條通用的規則,sed 可以用來將任意的可打印字符修改為任意其它的可打印字符。如果您想將不可打印字符修改為可打印字符—例如,鈴铛修改為單詞 "bell"—sed 不是適於完成這項工作的工具(但 tr 是)。
有時,您不想修改在一個文件中出現的所有指定項目。有時,您只想在滿足某些條件時才作修改—例如,在與其它一些數據匹配之後才作修改。為了說明這一點,請考慮以下文本文件:
$ cat sample_one
one 1
two 1
three 1
one 1
two 1
two 1
three 1
$
假定希望用 "2" 來替換 "1",但僅在單詞 "two" 之後才作替換,而不是每一行的所有位置。通過指定在給出替換命令之前必須存在一次匹配,可以實現這一點:
$ sed \'/two/ s/1/2/\' sample_one
one 1
two 2
three 1
one 1
two 2
two 2
three 1
$
現在,使其更加准確:
$ sed \'
> /two/ s/1/2/
> /three/ s/1/3/\' sample_one
one 1
two 2
three 3
one 1
two 2
two 2
three 3
$
請 再次記住唯一改變了的是顯示。如果您查看源文件,您將發現它始終保持不變。您必須將輸出保存至另一個文件,以實現永久保存。值得重復的是,不對源文件作修 改實際是禍中有福—它讓您能夠對文件進行試驗而不會造成任何實際的損害,直到讓正確命令以您預期和希望的方式進行工作。
以下命令將修改後的輸出保存至一個新的文件:
$ sed \'
> /two/ s/1/2/
> /three/ s/1/3/\' sample_one > sample_two
該輸出文件將所有修改合並在其中,並且這些修改通常將在屏幕上顯示。現在可以用 head、cat 或任意其它類似的實用工具來進行查看。
腳本文件
sed 工具允許您創建一個腳本文件,其中包含從該文件而不是在命令行進行處理的命令,並且 sed 工具通過 "-f" 選項來引用。通過創建一個腳本文件,您能夠一次又一次地重復運行相同的操作,並指定比每次希望從命令行進行處理的操作詳細得多的操作。
考慮以下腳本文件:
$ cat sedlist
/two/ s/1/2/
/three/ s/1/3/
$
現在可以在數據文件上使用腳本文件,獲得和我們之前看到的相同的結果:
$ sed -f sedlist sample_one
one 1
two 2
three 3
one 1
two 2
two 2
three 3
$
注意當調用 "-f" 選項時,在源文件內或命令行中不使用撇號。腳本文件,也稱為源文件,對於想重復多次的操作和從命令行運行可能出錯的復雜命令很有價值。編輯源文件並修改一個字符比在命令行中重新輸入一條多行的項目要容易得多。
限制行
編輯器默認查看輸入到流編輯器中的每一行,且默認在輸入到流編輯器中的每一行上進行編輯。這可以通過在發出命令之前指定約束條件來進行修改。例如,只在此示例文件的輸出的第 5 和第 6 行中用 "2" 來替換 "1",命令將為:
$ sed \'5,6 s/1/2/\' sample_one
one 1
two 1
three 1
one 1
two 2
two 2
three 1
$
在這種情況下,因為要修改的行是專門指定的,所以不需要替換命令。因此,您可以靈活地根據匹配准則(可以是行號或一種匹配模式)來選擇要修改哪些行(從根本上限制修改)。
禁止顯示
sed 默認將來自源文件的每一行顯示到屏幕上(或重定向到一個文件中),而無論該行是否受到編輯操作的影響,"-n" 參數覆蓋了這一操作。"-n" 覆蓋了所有的顯示,並且不顯示任何一行,而無論它們是否被編輯操作修改。例如:
$ sed -n -f sedlist sample_one
$
$ sed -n -f sedlist sample_one > sample_two
$ cat sample_two
$
在 第一個示例中,屏幕上不顯示任何東西。在第二個示例中,不修改任何東西,因此不將任何東西寫到新的文件中—它最後是空的。這不是否定了編輯的全部目的嗎? 為什麼這是有用的?它是有用的僅因為 "-n" 選項能夠被一條顯示命令 (-p) 覆蓋。為了說明這一點,假定現在像下面這樣對腳本文件進行了修改:
$ cat sedlist
/two/ s/1/2/p
/three/ s/1/3/p
$
然後下面是運行它的結果:
$ sed -n -f sedlist sample_one
two 2
three 3
two 2
two 2
three 3
$
保持不變的行全部不被顯示。只有受到編輯操作影響的行被顯示了。在這種方式下,可以僅取出這些行,進行修改,然後把它們放到一個單獨的文件中:
$ sed -n -f sedlist sample_one > sample_two
$
$ cat sample_two
two 2
three 3
two 2
two 2
three 3
$
利用它的另一種方法是只顯示一定數量的行。例如,只顯示 2-6 行,同時不做其它的編輯修改:
$ sed -n \'2,6p\' sample_one
two 1
three 1
one 1
two 1
two 1
$
其它所有的行被忽略,只有 2-6 行作為輸出顯示。這是一項出色的功能,其它任何工具都不能容易地實現。Head 將顯示一個文件的頂部,而 tail 將顯示一個文件的底部,但 sed 允許從任意位置取出想要的任意內容。