文本處理三劍客之sed

sed 編輯器是 Linux 系統管理員的工具包中最有用的資產之一,所以,有必要完全地瞭解其應用。正則表達式

Linux 操做系統最大的一個好處是它帶有各類各樣的實用工具。存在如此之多不一樣的實用工具,幾乎不可能知道並瞭解全部這些工具。能夠簡化關鍵狀況下操做的一個實用工具是 sed。它是任何管理員的工具包中最強大的工具之一,而且能夠證實它本身在關鍵狀況下很是有價值。shell

sed 實用工具是一個「編輯器」,但它與其它大多數編輯器不一樣。除了不面向屏幕以外,它仍是非交互式的。這意味着您必須將要對數據執行的命令插入到命令行或要處理的腳本中。當顯示它時,請忘記您在使用 Microsoft Word 或其它大多數編輯器時擁有的交互式編輯文件功能。sed 在一個文件(或文件集)中非交互式、而且不加詢問地接收一系列的命令並執行它們。於是,它流經文本就如同水流經溪流同樣,於是 sed 恰當地表明瞭流編輯器。它能夠用來將全部出現的 「Mr. Smyth」 修改成 「Mr. Smith」,或將 「tiger cub」 修改成 「wolf cub」。流編輯器很是適合於執行重複的編輯,這種重複編輯若是由人工完成將花費大量的時間。其參數可能和一次性使用一個簡單的操做所需的參數同樣有限,或者和一個具備成千上萬行要進行編輯修改的腳本文件同樣複雜。sed Linux UNIX 工具箱中最有用的工具之一,且使用的參數很是少。緩存

 

sed工做流程編輯器

一、 讀新的一行到緩存空間。工具

二、 從指定的操做指令中取出第一條指令,判斷是否匹配patternthis

三、 若是不匹配則忽略後續的編輯命令,回到2步繼續取出下一條指令spa

四、 若是匹配,則針對緩存的行執行後續的編輯命令;完成後,回到第2步繼續取出下一條指令;操作系統

五、 當全部指令都應用以後,輸出緩存行的內容;回到第1步繼續讀入下一行內容 命令行

六、 當全部行都處理完以後,結束;regexp

因而可知,sed並不是是將一個編輯命令分別應用到每一行,而後再取下一個編輯命令。偏偏相反,sed是以行的方式來處理的。另一方面,每一行都是被讀入到一塊緩存空間,該空間名爲模式空間(pattern space),這是一個很重要的概念,在後文中會屢次被說起。所以sed操做的都是最初行的拷貝,同時後續的編輯命令都是應用到前面的命令編輯後輸出的結果,因此編輯命令之間的順序就顯得格外重要。

 

sed語法

sed [options] '{Addredd command}' [filename]

option:

-n:只打印匹配的行

-i:直接修改讀取的的文件內容

-r:使用擴展的正則表達式

-e script –e script:能夠同時執行多個腳本

-f:直接將sed的動做寫在一個文件內 –f filename則可運行filename內的sed動做

Address

StartLine,EndLine  好比 1,100   $:最後一行

/regexp/: 例如 /^oot/

/pattern1/,/pattern2/ 第一次被pattern1匹配的行到第一次被pattern2匹配的行結束。

LineNum:指定的行

first~step: first行開始,每隔step行執行一次命令

addr1,+N: 從匹配的addr1行開始,連續N行,固然包括addr1這一行

command

a :新增, a 的後面能夠接字串,而這些字串會在新的一行出現(目前的下一行)

c :取代, c 的後面能夠接字串,這些字串能夠取代 n1,n2 之間的行!

d :刪除,由於是刪除啊,因此 d 後面一般不接任何咚咚;

i  :插入, i 的後面能夠接字串,而這些字串會在新的一行出現(目前的上一行)

p :列印,亦即將某個選擇的數據印出。一般 p 會與參數 sed -n 一塊兒運行~

s :取代,能夠直接進行取代的工做哩!一般這個 s 的動做能夠搭配正規表示法!例如 1,20s/old/new/g 就是啦!

 

下面開始介紹sed的基礎用法

替換命令

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

若是須要對同一文件或行做屢次修改,能夠有三種方法來實現它。第一種是使用 「-e」 選項,它通知程序使用了多條編輯命令。例如:  

注意若是輸入是源自以前的命令輸出,則不須要指定文件名一樣的原則也適用於 awksort 和其它大多數 Linux\UNIX 命令行實用工具程序。

屢次修改

$ 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

請再次記住惟一改變了的是顯示。若是您查看源文件,您將發現它始終保持不變。您必須將輸出保存至另外一個文件,以實現永久保存。值得重複的是,不對源文件做修改實際是禍中有福它讓您可以對文件進行試驗而不會形成任何實際的損害,直到讓正確命令以您預期和但願的方式進行工做。

如下命令將修改後的輸出保存至一個新的文件:

該輸出文件將全部修改合併在其中,而且這些修改一般將在屏幕上顯示。如今能夠用 headcat 或任意其它相似的實用工具來進行查看。

腳本文件

sed 工具容許您建立一個腳本文件,其中包含從該文件而不是在命令行進行處理的命令,而且 sed 工具經過 「-f」 選項來引用。經過建立一個腳本文件,您可以一次又一次地重複運行相同的操做,並指定比每次但願從命令行進行處理的操做詳細得多的操做。

考慮如下腳本文件:

 

如今能夠在數據文件上使用腳本文件,得到和咱們以前看到的相同的結果:

注意當調用 「-f」 選項時,在源文件內或命令行中不使用撇號。腳本文件,也稱爲源文件,對於想重複屢次的操做和從命令行運行可能出錯的複雜命令頗有價值。編輯源文件並修改一個字符比在命令行中從新輸入一條多行的項目要容易得多。

限制行

編輯器默認查看輸入到流編輯器中的每一行,且默認在輸入到流編輯器中的每一行上進行編輯。

能夠經過定址來定位你所但願編輯的行,該地址用數字構成,用逗號分隔的兩個行數表示以這兩行爲起止的行的範圍(包括行數表示的那兩行)。如13表示123行,美圓符號($)表示最後一行。範圍能夠經過數據,正則表達式或者兩者結合的方式肯定 。

例如,只在此示例文件的輸出的第 5 和第 6 行中用 「2″ 來替換 「1″,命令將爲:

 

在這種狀況下,由於要修改的行是專門指定的,因此不須要替換命令。所以,您能夠靈活地根據匹配準則(能夠是行號或一種匹配模式)來選擇要修改哪些行(從根本上限制修改)。

禁止顯示

sed 默認未來自源文件的每一行顯示到屏幕上(或重定向到一個文件中),而不管該行是否受到編輯操做的影響,」-n」 參數覆蓋了這一操做。」-n」 覆蓋了全部的顯示,而且不顯示任何一行,而不管它們是否被編輯操做修改。例如:

 

在第一個示例中,屏幕上不顯示任何東西。在第二個示例中,不修改任何東西,所以不將任何東西寫到新的文件中它最後是空的。這不是否認了編輯的所有目的嗎?爲何這是有用的?它是有用的僅由於 「-n」 選項可以被一條顯示命令 (-p) 覆蓋。爲了說明這一點,假定如今像下面這樣對腳本文件進行了修改:

 

而後下面是運行它的結果:

保持不變的行所有不被顯示。只有受到編輯操做影響的行被顯示了。在這種方式下,能夠僅取出這些行,進行修改,而後把它們放到一個單獨的文件中:

 

利用它的另外一種方法是隻顯示必定數量的行。例如,只顯示 2-6 行,同時不作其它的編輯修改:

其它全部的行被忽略,只有 2-6 行做爲輸出顯示。這是一項出色的功能,其它任何工具都不能容易地實現。Head 將顯示一個文件的頂部,而 tail 將顯示一個文件的底部,但 sed 容許從任意位置取出想要的任意內容。

刪除行

用一個值替換另外一個值遠非流編輯器能夠執行的惟一功能。它還具備許多的潛在功能,在我看來第二種最經常使用的功能是刪除。刪除與替換的工做方式相同,只是它刪除指定的行(若是您想要刪除一個單詞而不是一行,不要考慮刪除,而應考慮用空的內容來替換它―s/cat//)。

該命令的語法是: 

‘{what to find} d’

sample_one 文件中刪除包含 「two」 的全部行:

 

只顯示剩下的行,前三行不在顯示屏中出現。對於流編輯器,通常當它們涉及到全局表達式時,特別是應用於刪除操做時,有幾點要記住:

上三角號 (^) 表示一行的開始,所以,若是 「two」 是該行的頭三個字符,則

 

sed '/^two/ d' sample_one

將只刪除該行。
美圓符號 ($) 表明文件的結尾,或一行的結尾,所以,若是 「two」 是該行的最後三個字符,則

sed '/two$/ d' sample_one

將只刪除該行。

將這二者結合在一塊兒的結果:

 

sed '/^$/ d' {filename}

刪除文件中的全部空白行。例如,如下命令將 「1″ 替換爲 「2″,以及將 「1″ 替換爲 「3″,並刪除文件中全部尾隨的空行:

 

其一般的用途是刪除一個標題。如下命令將刪除文件中全部的行,從第一行直到第一個空行:

 

sed '1,/^$/ d' {filename}

添加和插入文本

能夠結合使用 sed 「a」 選項將文本添加到一個文件的末尾。實現方法以下:

 

1

$ sed '$a\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

three 1

 

 

 

7

one 1

8

two 1

 

 

 

9

two 1

 

10

three 1

 

 

 

11

This is where we stop

12

the test

 

 

 

 

13

$

 

在該命令中,美圓符號 ($) 表示文本將被添加到文件的末尾。反斜線 (\) 是必需的,它表示將插入一個回車符。若是它們被遺漏了,則將致使一個錯誤,顯示該命令是錯亂的;在任何要輸入回車的地方您必須使用反斜線。

 

要將這些行添加到第 4 和第 5 個位置而不是末尾,則命令變爲:

 

1

$ sed '3a\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

three 1

 

 

 

7

This is where we stop

8

the test

 

 

 

 

9

one 1

 

10

two 1

 

 

 

11

two 1

 

12

three 1

 

 

 

13

$

 

這將文本添加到第 3 行以後。和幾乎全部的編輯器同樣,您能夠選擇插入而不是添加(若是您但願這樣的話)。這二者的區別是添加跟在指定的行以後,而插入從指定的行開始。當用插入來代替添加時,只需用 「i」 來代替 「a」,以下所示:

 

1

$ sed '3i\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

This is where we stop

 

 

 

7

the test

8

three 1

 

 

 

 

9

one 1

 

10

two 1

 

 

 

11

two 1

 

12

three 1

 

 

 

13

$

 

新的文本出如今輸出的中間位置,而處理一般在指定的操做執行之後繼續進行。

 

讀寫文件

 

重定向輸出的功能已經演示過了,但須要指出的是,在編輯命令運行期間能夠同步地讀入和寫出文件。例如,執行替換,並將 1-3 行寫到名稱爲 sample_three 的文件中:

 

1

$ sed '

 

2

> /two/ s/1/2/

 

 

 

3

> /three/ s/1/3/

 

4

> 1,3 w sample_three' sample_one

 

 

 

5

one 1

6

two 2

 

 

 

7

three 3

8

one 1

 

 

 

 

9

two 2

 

10

two 2

 

 

 

11

three 3

12

$

 

 

 

 

13

 

 

14

$ cat sample_three

 

 

 

15

one 1

16

two 2

 

 

 

17

three 3

18

$

 

 

因爲爲 w (write) 命令指定了 「1,3″,因此只有指定的行被寫到了新文件中。不管被寫的是哪些行,全部的行都在默認輸出中顯示。

 

修改命令

 

除了替換項目以外,還能夠將行從一個值修改成另外一個值。要記住的是,替換是對字符逐個進行,而修改功能與刪除相似,它影響整行:

 

1

$ sed '/two/ c\

 

2

> We are no longer using two' sample_one

 

 

 

3

one 1

 

4

We are no longer using two

 

 

 

5

three 1

6

one 1

 

 

 

 

7

We are no longer using two

8

We are no longer using two

 

 

 

9

three 1

10

$

 

 

修改命令與替換的工做方式很類似,但在範圍上要更大些將一個項目徹底替換爲另外一個項目,而不管字符內容或上下文。誇張一點講,當使用替換時,只有字符 「1″ 被字符 「2″ 替換,而當使用修改時,原來的整行將被修改。在兩種狀況下,要尋找的匹配條件都僅爲 「two」

 

修改所有但……

 

對於大多數 sed 命令,詳細說明各類功能要進行何種修改。利用感嘆號,能夠在除指定位置以外的任何地方執行修改與默認的操做徹底相反。

 

例如,要刪除包含單詞 「two」 的全部行,操做爲:

 

1

$ sed '/two/ d' sample_one

2

one 1

 

 

 

 

3

three 1

4

one 1

 

 

 

 

5

three 1

6

$

 

 

而要刪除除包含單詞 「two」 的行以外的全部行,則語法變爲:

 

1

$ sed '/two/ !d' sample_one

2

two 1

 

 

 

 

3

two 1

4

two 1

 

 

 

5

$

 

若是您有一個文件包含一系列項目,而且想對文件中的每一個項目執行一個操做,那麼首先對那些項目進行一次智能掃描並考慮將要作什麼是很重要的。爲了使事情變得更簡單,您能夠將 sed 與任意迭代例程(forwhileuntil)結合來實現這一目的。

 

好比說,假定您有一個名爲 「animals」 的文件,其中包含如下項目:

 

1

pig

 

2

horse

 

 

 

3

elephant

4

cow

 

 

 

 

5

dog

6

cat

 

您但願運行如下例程:

 

1

#mcd.ksh

 

2

for I in $*

 

 

 

3

do

 

4

echo Old McDonald had a $I

 

 

 

5

echo E-I, E-I-O

6

done

 

 

結果將爲,每一行都顯示在 「Old McDonald has a」 的末尾。雖然對於這些項目的大部分這是正確的,但對於 「elephant」 項目,它有語法錯誤,由於結果應當爲 「an elephant」 而不是 「a elephant」。利用 sed,您能夠在來自 shell 文件的輸出中檢查這種語法錯誤,並經過首先建立一個命令文件來即時地更正它們:

 

1

#sublist

 

2

/ a a/ s/ a / an /

 

 

 

3

/ a e/ s/ a / an /

4

/a i/ s / a / an /

 

 

 

5

/a o/ s/ a / an /

6

/a u/ s/ a / an /

 

而後執行如下過程:

 

1

$ sh mcd.ksh 'cat animals' | sed -f sublist

 

如今,在運行了 mcd 腳本以後,sed 將在輸出中搜索單個字母 a (空格,」a」,空格)以後緊跟了一個元音的任意位置。若是這種位置存在,它將把該序列修改成空格,」an」,空格。這樣就使問題更正後才顯示在屏幕上,並確保各處的編輯人員在晚上能夠更容易地入睡。結果是:

 

1

Old McDonald had a pig

2

E-I, E-I-O

 

 

3

Old McDonald had a horse

4

E-I, E-I-O

 

 

5

Old McDonald had an elephant

6

E-I, E-I-O

 

 

7

Old McDonald had a cow

8

E-I, E-I-O

 

 

9

Old McDonald had a dog

10

E-I, E-I-O

 

 

11

Old McDonald had a cat

12

E-I, E-I-O

 

 

提早退出

sed 默認讀取整個文件,並只在到達末尾時才中止。不過,您可使用退出命令提早中止處理。只能指定一條退出命令,而處理將一直持續直到知足調用退出命令的條件。

例如,僅在文件的前五行上執行替換,而後退出:

 

1

$ sed '

 

2

> /two/ s/1/2/

 

3

> /three/ s/1/3/

4

> 5q' sample_one

 

5

one 1

6

two 2

 

7

three 3

8

one 1

 

 

9

two 2

10

$

 

 

在退出命令以前的項目能夠是一個行號(如上所示),或者一條查找/匹配命令:

1

$ sed '

 

2

> /two/ s/1/2/

 

3

> /three/ s/1/3/

 

4

> /three/q' sample_one

 

5

one 1

6

two 2

 

7

three 3

8

$

 

 

您還可使用退出命令來查看超過必定標準數目的行,並增長比 head 中的功能更強的功能。例如,head 命令容許您指定您想要查看一個文件的前多少行默認數爲 10,但可使用從 1 99 的任意一個數字。若是您想查看一個文件的前 110 行,您用 head 不能實現這一目的,但用 sed 能夠:

sed 110q filename

 

處理問題

當使用 sed 時,要記住的重要事項是它的工做方式。它的工做方式是:讀入一行,在該行上執行它已知要執行的全部任務,而後繼續處理下一行。每一行都受給定的每個編輯命令的影響。

若是您的操做順序沒有十分完全地考慮清楚,那麼這可能會很麻煩。例如,假定您須要將全部的 「two」 項目修改成 「three」,而後將全部的 「three」 修改成 「four」

 

1

$ sed '

 

2

> /two/ s/two/three/

 

3

> /three/ s/three/four/' sample_one

4

one 1

 

 

5

four 1

6

four 1

 

7

one 1

 

8

four 1

 

9

four 1

 

10

four 1

 

11

$

 

最初讀取的 「two」 被修改成 「three」。而後它知足爲下一次編輯創建的準則,從而變爲 「four」。最終的結果不是想要的結果如今除了 「four」 沒有別的項目了,而原本應該有 「three」 「four」

當執行這種操做時,您必須很是用心地注意指定操做的方式,並按某種順序來安排它們,使得操做之間不會互相影響。例如:

1

$ sed '

 

2

> /three/ s/three/four/

 

3

> /two/ s/two/three/' sample_one

4

one 1

 

 

5

three 1

6

four 1

 

 

7

one 1

 

8

three 1

 

 

 

9

three 1

10

four 1

 

11

$

 

這很是有效,由於 「three」 值在 「two」 變成 「three」 以前獲得修改。

標籤和註釋

能夠在 sed 腳本文件中放置標籤,這樣一旦文件變得龐大,能夠更容易地說明正在發生的事情。存在各類各樣與這些標籤相關的命令,它們包括:
接下來的步驟

訪問並收藏 Linux 技術中心

閱讀 Dale Dougherty Arnold Robbins 的著做 sed & awk, 2nd Edition O’Reilly & Associates 出版社)。

: 冒號表示一個標籤名稱。例如:

:HERE

以冒號開始的標籤能夠由 「b」 「t」 命令處理。

b {label} 充當 「goto」 語句的做用,將處理髮送至前面有一個冒號的標籤。例如, 

b HERE

將處理髮送給行

:HERE 

若是緊跟 b 以後沒有指定任何標籤,則處理轉至腳本文件的末尾。

t {label} 只要自上次輸入行或執行一次 「t」 命令以來進行了替換操做,就轉至該標籤。和 「b」 同樣,若是沒有給定標籤名,則處理轉至腳本文件的末尾。 

# 符號做爲一行的第一個字符將使整行被看成註釋處理。註釋行與標籤不一樣,不能使用 b t 命令來轉到註釋行上。

相關文章
相關標籤/搜索