《linux sed用法詳解》一節給大家介紹了如何用 sed 命令的基本功能處理文本中的數據,所涵蓋的知識點,可以滿足日常大多數文本編輯需求。本節將介紹 sed 提供的一些高級功能,這些功能雖不常用,但知道這些功能的存在以及用法也是有必要的。
在學習 sed 命令的基礎功能時,你可能注意到了一個局限,即所有的 sed 命令都只是針對單行數據執行操作,在 sed 命令讀取緩沖區中的文本數據時,它會基于換行符的位置,將數據分成行,sed 會根據定義好的腳本命令一次處理一行數據。
但是,有時我們需要對跨多行的數據執行特定操作。比如說,在文本中查找一串字符串"http://c.biancheng.net",它很有可能出現在兩行中,每行各包含其中一部分。這時,如果用普通的 sed 編輯器命令來處理文本,就不可能發現這種被分開的情況。
幸運的是,sed 命令的設計人員已經考慮到了這種情況,并設計了對應的解決方案。sed 包含了三個可用來處理多行文本的特殊命令,分別是:
Next 命令(N):將數據流中的下一行加進來創建一個多行組來處理。
Delete(D):刪除多行組中的一行。
Print(P):打印多行組中的一行。
注意,以上命令的縮寫,都為大寫。
N 命令會將下一行文本內容添加到緩沖區已有數據之后(之間用換行符分隔),從而使前后兩個文本行同時位于緩沖區中,sed 命令會將這兩行數據當成一行來處理。
下面這個例子演示的 N 命令的功能:
[root@localhost ~]# cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
[root@localhost ~]# sed '/first/{ N ; s//n/ / }' data2.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.
在這個例子中,sed 命令查找含有單詞 first 的那行文本。找到該行后,它會用 N 命令將下一行合并到那行,然后用替換命令 s 將換行符替換成空格。結果是,文本文件中的兩行在 sed 的輸出中成了一行。
如果要在數據文件中查找一個可能會分散在兩行中的文本短語,如何實現呢?這里給大家一個實例:
[root@localhost ~]# cat data3.txt
On Tuesday, the Linux System
Administrator's group meeting will be held.
All System Administrators should attend.
Thank you for your attendance.
[root@localhost ~]# sed 'N ; s/System Administrator/Desktop User/' data3.txt
On Tuesday, the Linux Desktop User's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
用 N 命令將發現第一個單詞的那行和下一行合并后,即使短語內出現了換行,你仍然可以找到它,這是因為,替換命令在 System 和 Administrator之間用了通配符(.)來匹配空格和換行符這兩種情況。但當它匹配了換行符時,它就從字符串中刪掉了換行符,導致兩行合并成一行。這可能不是你想要的。
要解決這個問題,可以在 sed 腳本中用兩個替換命令,一個用來匹配短語出現在多行中的情況,一個用來匹配短語出現在單行中的情況,比如:
[root@localhost ~]# sed 'N
> s/System/nAdministrator/Desktop/nUser/
> s/System Administrator/Desktop User/
> ' data3.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
第一個替換命令專門查找兩個單詞間的換行符,并將它放在了替換字符串中。這樣就能在第一個替換命令專門在兩個檢索詞之間尋找換行符,并將其納入替換字符串。這樣就允許在新文本的同樣位置添加換行符了。
但這個腳本中仍有個小問題,即它總是在執行 sed 命令前將下一行文本讀入到緩沖區中,當它到了后一行文本時,就沒有下一行可讀了,此時 N 命令會叫 sed 程序停止,這就導致,如果要匹配的文本正好在最后一行中,sed 命令將不會發現要匹配的數據。
解決這個 bug 的方法是,將單行命令放到 N 命令前面,將多行命令放到 N 命令后面,像這樣:
[root@localhost ~]# sed '
> s/System/nAdministrator/Desktop/nUser/
> N
> s/System Administrator/Desktop User/
> ' data3.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
現在,查找單行中短語的替換命令在數據流的后一行也能正常工作,多行替換命令則會負責短語出現在數據流中間的情況。
sed 不僅提供了單行刪除命令(d),也提供了多行刪除命令 D,其作用是只刪除緩沖區中的第一行,也就是說,D 命令將緩沖區中第一個換行符(包括換行符)之前的內容刪除掉。
比如說:
[root@localhost ~]# cat data4.txt
On Tuesday, the Linux System
Administrator's group meeting will be held.
All System Administrators should attend.
[root@localhost ~]# sed 'N ; /System/nAdministrator/D' data4.txt
Administrator's group meeting will be held.
All System Administrators should attend.
文本的第二行被 N 命令加到了緩沖區,因此 sed 命令第一次匹配就是成功,而 D 命令會將緩沖區中第一個換行符之前(也就是第一行)的數據刪除,所以,得到了如上所示的結果。
下面的例子中,它會刪除數據流中出現在第一行前的空白行:
[root@localhost ~]# cat data5.txt
This is the header line.
This is a data line.
This is the last line.
[root@localhost ~]# sed '/^$/{N ; /header/D}' data5.txt
This is the header line.
This is a data line.
This is the last line.
sed會查找空白行,然后用 N 命令來將下一文本行添加到緩沖區。此時如果緩沖區的內容中含有單詞 header,則 D 命令會刪除緩沖區中的第一行。
同 d 和 D 之間的區別一樣,P(大寫)命令和單行打印命令 p(小寫)不同,對于具有多行數據的緩沖區來說,它只會打印緩沖區中的第一行,也就是首個換行符之前的所有內容。
例如,test.txt 文件中的內容如下:
[root@localhost ~]# cat test.txt
aaa
bbb
ccc
ddd
eee
fff
表 1 中是對 test.txt 文件中的內容分別用 p 命令和 P 命令后,產生的輸出信息的對比。
| P(大寫)命令 | p(小寫)命令 |
|---|---|
[root@localhost ~]# sed '/.*/N;P' | [root@localhost ~]# sed '/.*/N;p' |
第一個 sed 命令,每次都使用 N 將下一行內容追加到緩沖區內容的后面(用換行符間隔),也就是說,第一次時緩沖區中的內容為 aaa/nbbb,但 P(大寫) 命令的作用的打印換行符之前的內容,也就是 aaa,之后則是 sed 在自動輸出功能輸出 aaa 和 bbb(sed 命令會自動將 /n 輸出為換行),依次類推,就輸出了所看到的結果。
第二個 sed 命令,使用的是 p (小寫)單行打印命令,它會將緩沖區中的所有內容全部打印出來(/n 會自動輸出為換行),因此,出現了看到的結果。
前面我們一直說,sed 命令處理的是緩沖區中的內容,其實這里的緩沖區,應稱為模式空間。值得一提的是,模式空間并不是 sed 命令保存文件的唯一空間。sed 還有另一塊稱為保持空間的緩沖區域,它可以用來臨時存儲一些數據。
表 2 列出了 5 條可用來操作保持空間的命令。
| 命令 | 功能 |
|---|---|
| h | 將模式空間中的內容復制到保持空間 |
| H | 將模式空間中的內容附加到保持空間 |
| g | 將保持空間中的內容復制到模式空間 |
| G | 將保持空間中的內容附加到模式空間 |
| x | 交換模式空間和保持空間中的內容 |
通常,在使用 h 或 H 命令將字符串移動到保持空間后,最終還要用 g、G 或 x 命令將保存的字符串移回模式空間。保持空間最直接的作用是,一旦我們將模式空間中所有的文件復制到保持空間中,就可以清空模式空間來加載其他要處理的文本內容。
由于有兩個緩沖區域,下面的例子中演示了如何用 h 和 g 命令來將數據在 sed 緩沖區之間移動。
[root@localhost ~]# cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
[root@localhost ~]# sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt
This is the first data line.
This is the second data line.
This is the first data line.
這個例子的運行過程是這樣的:
sed腳本命令用正則表達式過濾出含有單詞first的行;
當含有單詞 first 的行出現時,h 命令將該行放到保持空間;
p 命令打印模式空間也就是第一個數據行的內容;
n 命令提取數據流中的下一行(This is the second data line),并將它放到模式空間;
p 命令打印模式空間的內容,現在是第二個數據行;
g 命令將保持空間的內容(This is the first data line)放回模式空間,替換當前文本;
p 命令打印模式空間的當前內容,現在變回第一個數據行了。
通常,sed 程序的執行過程會從第一個腳本命令開始,一直執行到最后一個腳本命令(D 命令是個例外,它會強制 sed 返回到腳本的頂部,而不讀取新的行)。sed 提供了 b 分支命令來改變命令腳本的執行流程,其結果與結構化編程類似。
b 分支命令基本格式為:
[address]b [label]
其中,address 參數決定了哪些行的數據會觸發分支命令,label 參數定義了要跳轉到的位置。
需要注意的是,如果沒有加 label 參數,跳轉命令會跳轉到腳本的結尾,比如:
[root@localhost ~]# cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
[root@localhost ~]# sed '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt
Is this the header test?
This is the first data line.
This is the second data line.
Is this the last test?
可以看到,因為 b 命令未指定 label 參數,因此數據流中的第2行和第3行并沒有執行那兩個替換命令。
如果我們不想直接跳到腳本的結尾,可以為 b 命令指定一個標簽(也就是格式中的 label,最多為 7 個字符長度)。在使用此該標簽時,要以冒號開始(比如 :label2),并將其放到要跳過的腳本命令之后。這樣,當 sed 命令匹配并處理該行文本時,會跳過標簽之前所有的腳本命令,但會執行標簽之后的腳本命令。
比如說:
[root@localhost ~]# sed '{/first/b jump1 ; s/This is the/No jump on/
> :jump1
> s/This is the/Jump here on/}' data2.txt
No jump on header line
Jump here on first data line
No jump on second data line
No jump on last line
在這個例子中,如果文本行中出現了 first,程序的執行會直接跳到 jump1 標簽之后的腳本行。如果分支命令的模式沒有匹配,sed 會繼續執行所有的腳本命令。
b 分支命令除了可以向后跳轉,還可以向前跳轉,例如:
[root@localhost ~]# echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> /,/b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
在這個例子中,當緩沖區中的行內容中有逗號時,腳本命令就會一直循環執行,每次迭代都會刪除文本中的第一個逗號,并打印字符串,直至內容中沒有逗號。
類似于 b 分支命令,t 命令也可以用來改變 sed 腳本的執行流程。t 測試命令會根據 s 替換命令的結果,如果匹配并替換成功,則腳本的執行會跳轉到指定的標簽;反之,t 命令無效。
測試命令使用與分支命令相同的格式:
[address]t [label]
跟分支命令一樣,在沒有指定標簽的情況下,如果 s 命令替換成功,sed 會跳轉到腳本的結尾(相當于不執行任何腳本命令)。例如:
[root@localhost ~]# sed '{
> s/first/matched/
> t
> s/This is the/No match on/
> }' data2.txt
No match on header line
This is the matched data line
No match on second data line
No match on last line
此例中,第一個替換命令會查找模式文本 first,如果匹配并替換成功,命令會直接跳過后面的替換命令;反之,如果第一個替換命令未能匹配成功,第二個替換命令就會被執行。
再舉個例子:
[root@localhost ~]# echo "This, is, a, test, to, remove, commas. " | sed -n '{
> :start
> s/,//1p
> t start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
新聞熱點
疑難解答