十二、sed文本處理


 

 

一、概述

1.sed 是一款流編輯工具,用來對文本進行過濾與替換工作,特別是當你想要對幾十個配置文件做統計修改時,你會感受到 sed 的魅力!
sed 通過輸入讀取文件內容,但一次僅讀取一行內容進行某些指令處理后輸出,所以 sed更適合於處理大數據文件。
2.sed 流程:
* 通過文件或管道讀取文件內容。
* sed 並不直接修改源文件,而是將讀入的內容復制到緩沖區中,我們稱之為模式空間(pattern space)。
* 根據 sed 的指令對模式空間中的內容進行處理並輸出結果,默認輸出至標准輸出即屏幕上。

 

 

二、sed 語法和基本命令



 

基本用法

 

sed [options] {sed-commands} {input-file}

 

 sed 每次從 input-file 中讀取一行記錄,並在該記錄上執行 sed-commands
sed 首先從 input-file 中讀取第一行,然后執行所有的 sed-commands;再讀取第二行,執行所有 sed-commands,重復這個過程,直到 input-file 結束
通過制定[options] 還可以給 sed 傳遞一些可選的選項

 

 

 

常見命令選項;改變數據處理的方式

 

 

命令選項    注釋
-n    屏蔽默認輸出(全部文本),不再默認顯示模式空間中的內容
-i,--in-place      直接修改源文件內容
-f    -f script-file,--file=script-file              從文件中讀取腳本指令,對編寫自動腳本程序來說很棒!
-e SCRIPT -e SCRIPT:可以同時執行多個腳本
-r    啟用擴展的正則表達式,若與其他選項一起使用,應作為首個選項
{}    可組合多個命令,以分號分隔
--version                     顯示 sed 版本。
--help                           顯示幫助文檔。
-l N, --line-length=N       該選項指定 l 指令可以輸出的行長度,l 指令用於輸出非打印字符。
--posix                     禁用 GNU sed 擴展功能。

 

下面的例子演示了 sed 的基本語法,它打印出/etc/passwd 文件中的所有行

sed –n 'p' /etc/passwd


該例子的重點在於, {sed-commands}既可以是單個命令,也可以是多個命令。你也可以把多個 sed 命令合並到一個文件中,這個文件被稱為 sed 腳本,然后使用-f 選項調用它,如下面的例子:
使用 sed 腳本的基本語法:

sed [ options ] –f {sed-commands-in-a-file} {input-file}

 


下面的例子演示了使用 sed 腳本的用法,這個例子將打印/etc/passwd 問中以 root nobody
開頭的行:

vi test-script.sed
/^root/ p
/^nobody/ p
$ sed –n –f test-script.sed /etc/passwd

 


你也可以使用 –e 選項,執行多個 sed 命令,如下所示:
-e 的使用方法

sed [ options ] –e {sed-command-1} –e {sed-command-2} {input-file}

 

下面的例子演示了-e 的使用方法,它打印/etc/passwd 中以 root nobody 開頭的行:

[root@ceph-node5 ~]# sed -n -e '/^root/ p' -e '/^nobody/ p' /etc/passwd 

 


如果使用-e 執行多個命令,也可以使用反斜杠\把它們分割到多行執行:

sed -n \
-e '/^root/ p' \
-e '/^nobody/ p' \
/etc/passwd

 


也可以使用{ }將多個命令分組執行:
{}的使用方法:

sed [options] '{
sed-command-1
sed-command-2
}' input-file

 


下面的例子演示了{}的使用方法,打印/etc/passwd 中以 root nobody 開頭的行:

sed -n '{
/^root/ p
/^nobody/ p
}' /etc/passwd

 


注意:sed 絕不會修改原始文件 input-file,它只是將結果內容輸出到標准輸出設備。 如果要保持變更,應該使用重定向 > filename.txt

 

 

Sed 腳本執行流程

Sed 腳本執行遵從下面簡單易記的順序: Read,Execute,Print,Repeat(讀取,執行,打印,重復)
簡稱 REPR
分析腳本執行順序:

讀取一行到模式空間(sed 內部的一個臨時緩存,用於存放讀取到的內容)
在模式空間中執行命令。如果使用了{ } 或 –e 指定了多個命令, sed 將依次執行每個命令
打印模式空間的內容,然后清空模式空間
重復上述過程,直到文件結束

 

如圖:

 

 

 

 

打印模式空間(命令 p)

 

 

所有的示例都要用到下面的 employee.txt 文件,請先行創建該文件。

[root@ceph-node5 ~]#  vim employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

使用命令 p,可以打印當前模式空間的內容。
sed 在執行完命令后會默認打印模式空間的內容,既然如此,那么你可能會問為何還需要命令 p 呢。
有如下原因,命令 p 可以控制只輸出你指定的內容。通常使用 p 時,還需要使用-n 選項來
屏蔽 sed 的默認輸出,否則當執行命令 p 時,每行記錄會輸出兩次。

[root@ceph-node5 ~]# sed 'p' employee.txt
101,John Doe,CEO
101,John Doe,CEO
102,Jason Smith,IT Manager
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
105,Jane Miller,Sales Manager

 

輸出 employee.txt 的內容,只打印一行(cat employee.txt 命令作用相同):

[root@ceph-node5 ~]# sed -n 'p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

定址符

 

 

定址符,即[地址1,[地址2]]
是可選項,用來指定處理的起、止行數
不指定定址符時默認處理全部文本
定址符的表示方式可以使用行號和正則表達式

 

 例如:

Address:
1、StartLine,EndLine
    比如1,100
    $:最后一行
2、/RegExp/
    /^root/
3、/pattern1/,/pattern2/
    第一次被pattern1匹配到的行開始,至第一次被pattern2匹配到的行結束,這中間的所有行
4、LineNumber
    指定的行
5、StartLine, +N
    從startLine開始,向后的N行;

 

如果在命令前面不指定地址范圍,那么默認會匹配所有行。 下面的例子,在命令前面指定了地址范圍:

只打印第 2 :

[root@ceph-node5 ~]#  sed -n '2p' employee.txt  
102,Jason Smith,IT Manager

 


打印第 1 至第 4 :

[root@ceph-node5 ~]# sed -n '1,4 p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


打印第 2 行至最后一行($代表最后一行):

[root@ceph-node5 ~]#  sed -n '2,$ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

修改地址范圍

 

可以使用逗號、加號、和波浪號來修改地址范圍。
上面的例子里面,就已經使用了逗號參與地址范圍的指定。其意思很明了: n,m 代表第 n 至第 m 行。
加號+配合逗號使用,可以指定相的若干行,而不是絕對的幾行。如 n,+m 表示從第 n 行開始后的 m
波浪號~也可以指定地址范圍。 它指定每次要跳過的行數。如 n~m 表示從第 n 行開始,每次跳過 m 行:

1~2 匹配 1,3,5,7,……
2~2 匹配 2,4,6,8,……
1~3 匹配 1,4,7,10,…..
2~3 匹配 2,5,8,11,…..

 

 只打印奇數行

 

[root@ceph-node5 ~]# sed -n '1~2 p' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 


模式匹配


一如可以使用數字指定地址(或地址范圍),也可以使用一個模式(或模式范圍)來匹配,如下面的例子所示。
打印匹配模式"Jane"的行:

[root@ceph-node5 ~]# sed -n '/Jane/ p' employee.txt
105,Jane Miller,Sales Manager

 


打印第一次匹配 Jason 的行至第 4 行的內容:

[root@ceph-node5 ~]#  sed -n '/Jason/,4 p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 



注意:如果開始的 4 行中,沒有匹配到 Jason,那么 sed 會打印第 4 行以后匹配到 Jason 的內容


打印從第一次匹配 Raj 的行到最后的所有行:

[root@ceph-node5 ~]# sed -n '/Raj/,$ p' employee.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


打印自匹配 Raj 的行開始到匹配 Jane 的行之間的所有內容:

[root@ceph-node5 ~]# sed -n '/Raj/,/Jane/ p' employee.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 



打印匹配 Jason 的行和其后面的兩行:

[root@ceph-node5 ~]#  sed -n '/Jane/,+2 p' employee.txt
105,Jane Miller,Sales Manager

 

 

刪除行

 

命令 d 用來刪除行,需要注意的是它只刪除模式空間的內容,和其他 sed 命令一樣,命令 d不會修改原始文件的內容。
如果不提供地址范圍, sed 默認匹配所有行,所以下面的例子什么都不會輸出,因為它匹配了所有行並刪除了它們:

sed 'd' employee.txt


指定要刪除的地址范圍更有用,下面是幾個例子:
只刪除第 2 :

[root@ceph-node5 ~]# sed '2 d' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


刪除第 1 至第 4 行:

[root@ceph-node5 ~]# sed '1,4 d' employee.txt
105,Jane Miller,Sales Manager

 

刪除第 2 行至最后一行:

[root@ceph-node5 ~]# sed '2,$ d' employee.txt
101,John Doe,CEO

 


只刪除奇數行:

[root@ceph-node5 ~]#  sed '1~2 d' employee.txt
102,Jason Smith,IT Manager
104,Anand Ram,Developer

 



刪除匹配 Manager 的行:

[root@ceph-node5 ~]#  sed '/Manager/ d' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


刪除從第一次匹配 Jason 的行至第 4 行:

[root@ceph-node5 ~]# sed '/Jason/,4 d' employee.txt
101,John Doe,CEO
105,Jane Miller,Sales Manager


如果開頭的 4 行中,沒有匹配 Jason 的行,那么上述命令將刪除第 4 行以后匹配 Manager的行

 


刪除從第一次匹配 Raj 的行至最后一行:

[root@ceph-node5 ~]# sed '/Raj/,$ d' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


刪除第一次匹配 Jason 的行和緊跟着它后面的兩行:

[root@ceph-node5 ~]# sed '/Jason/,+2 d' employee.txt
101,John Doe,CEO
105,Jane Miller,Sales Manager

 



常用的刪除命令示例:
刪除所有空行

[root@ceph-node5 ~]# sed '/^$/ d' employee.txt

 


刪除所有注釋行(假定注釋行以#開頭)

sed '/^#/' employee.txt

 

注意:如果有多個命令, sed 遇到命令 d 時,會刪除匹配到的整行數據,其余的命令將無法操作被刪除的行。

 

 

把模式空間內容寫到文件中(w 命令)

 

命令 w 可以把當前模式空間的內容保存到文件中。默認情況下模式空間的內容每次都會打印到標准輸出,如果要把輸出保存到文件同時不顯示到屏幕上,還需要使用-n 選項。
下面是幾個例子:
employee.txt 的內容保存到文件 output.txt,同時顯示在屏幕上

[root@ceph-node5 ~]# sed 'w output.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager


employee.txt 的內容保存到文件 output.txt,但不在屏幕上顯示

[root@ceph-node5 ~]#  sed -n 'w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


只保存第 2 行:

[root@ceph-node5 ~]# sed -n '2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager


保存第 1 至第 4 行:

[root@ceph-node5 ~]# sed -n '1,4 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer


保存第 2 行起至最后一行:

[root@ceph-node5 ~]# sed -n '2,$ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


只保存奇數行:

[root@ceph-node5 ~]# sed -n '1~2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 



保存匹配 Jane 的行:

[root@ceph-node5 ~]# sed -n '/Jane/ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
105,Jane Miller,Sales Manager

 



保存第一次匹配 Jason 的行至第 4 行:

[root@ceph-node5 ~]# sed -n '/Jason/,4 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

注意: 如果開始的 4 行里沒有匹配到 Jason,那么該命令只保存第 4 行以后匹配到 Jason

 


保存第一次匹配 Raj 的行至最后一行:

[root@ceph-node5 ~]#  sed -n '/Raj/,$ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


保存匹配 Raj 的行至匹配 Jane 的行:

[root@ceph-node5 ~]# sed -n '/Raj/,/Jane/ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


保存匹配 Jason 的行以及緊跟在其后面的兩行:

[root@ceph-node5 ~]# sed -n '/Jason/,+2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

 

 

三、sed 替換命令

 

 流編輯器中最強大的功能就是替換(substitute),

 

sed 替換命令語法

 

 

sed '[address-range|pattern-range] s/original-string/replacement-string/[substitute-flags]' input file

 

 上面提到的語法為:

 

address-range 或 pattern-range(即地址范圍和模式范圍)是可選的。 如果沒有指定,那么 sed 將在所有行上進行替換。
s 即執行替換命令 substitute
original-string 是被 sed 搜索然后被替換的字符串,它可以是一個正則表達式
replacement-string 替換后的字符串
substitute-flags 是可選的,下面會具體解釋

 

請謹記原始輸入文件不會被修改, sed 只在模式空間中執行替換命令,然后輸出模式空間的內容。
下面是一些簡單的替換示例(替換的部分用黑體標明)
Director 替換所有行中的 Manager:

 

[root@ceph-node5 ~]# sed 's/Manager/Director/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Director
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Director

 


只把包含 Sales 的行中的 Manager 替換為 Director:

[root@ceph-node5 ~]# sed '/Sales/s/Manager/Director/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Director

 

注意: 本例由於使用了地址范圍限制,所以只有一個 Manager 被替換了

 

 

全局標志 g

 

 

 

g 代表全局(global) 默認情況下, sed 至會替換每行中第一次出現的 original-string。如果你要替換每行中出現的所有 original-string,就需要使用 g


用大寫 A 替換第一次出現的小寫字母 a

[root@ceph-node5 ~]#  sed 's/a/A/' employee.txt
101,John Doe,CEO
102,JAson Smith,IT Manager
103,RAj Reddy,Sysadmin
104,AnAnd Ram,Developer
105,JAne Miller,Sales Manager

 



把所有小寫字母 a 替換為大寫字母 A

[root@ceph-node5 ~]# sed 's/a/A/g' employee.txt
101,John Doe,CEO
102,JAson Smith,IT MAnAger
103,RAj Reddy,SysAdmin
104,AnAnd RAm,Developer
105,JAne Miller,SAles MAnAger


注意:上述例子會在所有行上替換,因為沒有指定地址范圍。

 

 數字標志(1,2,3 ….)

 

使用數字可以指定 original-string 出現的次序。 只有第 n 次出現的 original-string 才會觸發替換。 每行的數字從 1 開始,最大為 512
比如/11 會替換每行中第 11 次出現的 original-string
下面是幾個例子:
把第二次出現的小寫字母 a 替換為大寫字母 A

[root@ceph-node5 ~]# sed 's/a/A/2' employee.txt
101,John Doe,CEO
102,Jason Smith,IT MAnager
103,Raj Reddy,SysAdmin
104,Anand RAm,Developer
105,Jane Miller,SAles Manager

 

為了方便,請先建立如下文件:

[root@ceph-node5 ~]# vim substitute-locate.txt
locate command is used to locate files
locate command uses database to locate files
locate command can also use regex for searching

 



使用剛才建立的文件,把每行中第二次出現的 locate 替換為 find:

[root@ceph-node5 ~]#  sed 's/locate/find/2' substitute-locate.txt
locate command is used to find files
locate command uses database to find files
locate command can also use regex for searching

 


注意:第 3 行中 locate 只出現了一次,所以沒有替換任何內容

 

打印標志 p(print)

 

 

命令 p 代表 print。當替換操作完成后,打印替換后的行。與其他打印命令類似, sed 中比較有用的方法是和-n 一起使用以抑制默認的打印操作。
只打印替換后的行:

[root@ceph-node5 ~]# sed -n 's/John/Johnny/p' employee.txt
101,Johnny Doe,CEO

 


在之前的數字標志的例子中,使用/2 來替換第二次出現的 locate。第 3 行中 locate 只出現了一次,所以沒有替換任何內容。使用 p 標志可以只打印替換過的兩行。


把每行中第二次出現的 locate 替換為 find 並打印出來:

[root@ceph-node5 ~]# sed -n 's/locate/find/2p' substitute-locate.txt
locate command is used to find files
locate command uses database to find files

 

 

寫標志 w

標志 w 代表 write。當替換操作執行成功后,它把替換后的結果保存的文件中。 多數人更傾向於使用 p 打印內容,然后重定向到文件中。

為了對 sed 標志有個完整的描述, 在這里把這個標志也提出來了

 

 

只把替換后的內容寫到 output.txt :

[root@ceph-node5 ~]# sed -n 's/John/Johnny/w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,Johnny Doe,CEO

 


和之前使用的命令 p 一樣,使用 w 會把替換后的內容保存到文件 output.txt 中。
把每行第二次出現的 locate 替換為 find,把替換的結果保存到文件中,同時顯示輸入文件所有內容:

[root@ceph-node5 ~]# sed 's/locate/find/2w output.txt' substitute-locate.txt
locate command is used to find files
locate command uses database to find files
locate command can also use regex for searching
[root@ceph-node5 ~]# cat output.txt
locate command is used to find files
locate command uses database to find files

 

 

忽略大小寫標志 i (ignore)

 

替換標志 i 代表忽略大小寫。 可以使用 i 來以小寫字符的模式匹配 original-string。 該標志只有 GNU Sed 中才可使用。
下面的例子不會把 John 替換為 Johnny,因為 original-string 字符串是小寫形式:

[root@ceph-node5 ~]# sed 's/john/Johnny/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 



john John 替換為 Johnny:

[root@ceph-node5 ~]#  sed 's/john/Johnny/i' employee.txt
101,Johnny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

 

執行命令標志 e (excuate)


替換標志 e 代表執行(execute)。該標志可以將模式空間中的任何內容當做 shell 命令執行,
並把命令執行的結果返回到模式空間。 該標志只有 GNU Sed 中才可使用。
下面是幾個例子:
為了下面的例子,請先建立如下文件:

[root@ceph-node5 ~]# vim files.txt
/etc/passwd
/etc/group

 



files.txt 文件中的每行前面添加 ls –l 並打擊結果:

[root@ceph-node5 ~]#  sed 's/^/ls -l/' files.txt
ls -l/etc/passwd
ls -l/etc/group

 


files.txt 文件中的每行前面添加 ls –l 並把結果作為命令執行:

[root@ceph-node5 ~]#  sed 's/^/ls -l /e' files.txt
-rw-r--r--. 1 root root 890 Oct 29 16:20 /etc/passwd
-rw-r--r--. 1 root root 457 Oct 29 16:20 /etc/group

 



使用替換標志組合


根據需要可以把一個或多個替換標志組合起來使用。
下面的例子將把每行中出現的所有 Manager manager 替換為 Director。然后把替換后的內容打印到屏幕上,同時把這些內容保存到 output.txt 文件中。
使用 g,i,p w 的組合:

[root@ceph-node5 ~]# sed -n 's/manager/Director/igpw output.txt' employee.txt
102,Jason Smith,IT Director
105,Jane Miller,Sales Director
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Director
105,Jane Miller,Sales Director

 

 

 

 

sed 替換命令分界符


上面所有例子中,我們都是用了 sed 默認的分界符/,s/original-string/replacement-string/g

如果在 original-string replacement-string 中有/,那么需要使用反斜杠\來轉義。為了便於示例,請先建立下面文件:

 

[root@ceph-node5 ~]# vim path.txt
reading /usr/local/bin directory

 


限制使用 sed /usr/local/bin 替換為/usr/bin。 在下面例子中, sed 默認的分界符/都被\轉義了:

[root@ceph-node5 ~]# sed 's/\/usr\/local\/bin/\/usr\/bin/' path.txt
reading /usr/bin directory


這很難看。 如果要替換一個很長的路徑,每個/前面都使用\轉義,會顯得很混亂。幸運的是,你可以使用任何一個字符作為 sed 替換命令的分界符,如 | ^ @ 或者 !
下面的所有例子都比較易讀。 這里不再顯示下面例子的結果,因為它們的結果和上面的例子是相同的。 我個人比較傾向於使用@或者!來替換路徑,但使用什么看你自己的偏好了。

sed 's|/usr/local/bin|/usr/bin|' path.txt
sed 's^/usr/local/bin^/usr/bin^' path.txt
sed 's@/usr/local/bin@/usr/bin@' path.txt
sed 's!/usr/local/bin!/usr/bin!' path.txt

 

 

 

單行內容上執行多個命令


一如之前所說, sed 執行的過程是讀取內容、執行命令、打印結果、重復循環。 其中執行命令部分,可以由多個命令執行, sed 將一個一個地依次執行它們。
例如,你有兩個命令, sed 將在模式空間中執行第一個命令,然后執行第二個命令。

如果第一個命令改變了模式空間的內容,第二個命令會在改變后的模式空間上執行(此時模式空間的內容已經不是最開始讀取進來的內容了)
下面的例子演示了在模式空間內執行兩個替換命令的過程:
Developer 替換為 IT Manager,然后把 Manager 替換為 Director:

[root@ceph-node5 ~]# sed '{
> s/Developer/IT Manager/
> s/Manager/Director/
>  }' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Director
103,Raj Reddy,Sysadmin
104,Anand Ram,IT Director
105,Jane Miller,Sales Director

 


我們來分析下第 4 行的執行過程:

1.讀取數據: 在這一步, sed 讀取內容到模式空間,此時模式空間的內容為:104,Anand Ram,Developer
2.執行命令: 第一個命令, s/Developer/IT Manager/執行后,模式空間的內容為:104,Anand Ram,IT Manager
現在在模式空間上執行第二個命令 s/Manager/Director/,執行后,模式空間內容為:104,Anand Ram,IT Director
謹記: sed 在第一個命令執行的結果上,執行第二個命令。
3.打印內容:打印當前模式空間的內容,如下:104,Anand Ram,IT Director
4.重復循環: 移動的輸入文件的下一行,然后重復執行第一步,即讀取數據 

 

 

&的作用——獲取匹配到的模式


當在 replacement-string 中使用&時,它會被替換成匹配到的 original-string 或正則表達式,這是個很有用的東西。
看下面示例:
給雇員 ID(即第一列的 3 個數字)加上[ ],101 改成[101]

[root@ceph-node5 ~]# sed 's/^[0-9][0-9][0-9]/[&]/g' employee.txt
[101],John Doe,CEO
[102],Jason Smith,IT Manager
[103],Raj Reddy,Sysadmin
[104],Anand Ram,Developer
[105],Jane Miller,Sales Manager

 



把每一行放進< >中:

[root@ceph-node5 ~]# sed 's/^.*/<&>/' employee.txt
<101,John Doe,CEO>
<102,Jason Smith,IT Manager>
<103,Raj Reddy,Sysadmin>
<104,Anand Ram,Developer>
<105,Jane Miller,Sales Manager>

 

 

分組替換(單個分組)


跟在正則表達式中一樣, sed 中也可以使用分組。分組以\(開始,以\)結束。 分組可以用在回溯引用中。
回溯引用即重新使用分組所選擇的部分正則表達式, 在 sed 替換命令的 replacement-string

中和正則表達式中,都可以使用回溯引用。
單個分組:

[root@ceph-node5 ~]# sed 's/\([^,]*\).*/\1/g' employee.txt
101
102
103
104
105

 



上面例子中:

正則表達式\([^,]*\)匹配字符串從開頭到第一個逗號之間的所有字符(並將其放入第一個分組中)
replacement-string 中的\1 將替代匹配到的分組
g 即是全局標志

 

下面這個例子只會顯示/etc/passwd 的第一列,即用戶名:

[root@ceph-node5 ~]#  sed 's/\([^:]*\).*/\1/' /etc/passwd

 


下面的例子,如果單詞第一個字符為大寫,那么會給這個大寫字符加上()

[root@ceph-node5 ~]# echo "The Geek Stuff"|sed 's/\(\b[A-Z]\)/\(\1\)/g'
(T)he (G)eek (S)tuff

 


請先建立下面文件,以便下面的示例使用:

[root@ceph-node5 ~]# vim numbers.txt
1
12
123
1234
12345
123456

 



格式化數字,增加其可讀性:

[root@ceph-node5 ~]# sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g' numbers.txt
1
12
123
1,234
12,345
123,456

 

 

分組替換(多個分組)


你可以使用多個\(\)划分多個分組,使用多個分組時,需要在 replacement-string 中使用\n來指定第 n 個分組。如下面的示例。
只打印第一列(雇員 ID)和第三列(雇員職位):

[root@ceph-node5 ~]#  sed 's/^\([^,]*\),\([^,]*\),\([^,]*\)/\1,\3/' employee.txt
101,CEO
102,IT Manager
103,Sysadmin
104,Developer
105,Sales Manager


在這個例子中,可以看到, original-string 中,划分了 3 個分組,以逗號分隔。

([^,]*\) 第一個分組,匹配雇員 ID,為字段分隔符
([^,]*\) 第二個分組,匹配雇員姓名,為字段分隔符
([^,]*\) 第三個分組,匹配雇員職位,為字段分隔符,
上面的例子演示了如何使用分組 \
1 代表第一個分組(雇員 ID),出現在第一個分組之后的逗號 \3 代表第二個分組(雇員職位)

 

注意: sed 最多能處理 9 個分組,分別用\1 \9 表示。

交換第一列(雇員 ID)和第二列(雇員姓名)

[root@ceph-node5 ~]# sed 's/^\([^,]*\),\([^,]*\),\([^,]*\)/\2,\1,\3/' employee.txt
John Doe,101,CEO
Jason Smith,102,IT Manager
Raj Reddy,103,Sysadmin
Anand Ram,104,Developer
Jane Miller,105,Sales Manager

 

 

 

 

GNU Sed 專有的替換標志

 

 


下面的標志,只有 GNU 版的 sed 才能使用。 它們可以用在替換命令中的 replacement-string里面.

 

 

\l 標志

 

 


當在 replacement-string 中使用\l 標志時,它會把緊跟在其后面的字符當做小寫字符來處理。
如你所知,下面的例子將把 John 換成 JOHNNY:

[root@ceph-node5 ~]# sed 's/John/JOHNNY/' employee.txt  
101,JOHNNY Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


下面的例子,在 replacement-string 中的 H 前面放置了\l 標志,它會把 JOHNNY 中的 H 換成小寫的 h:

[root@ceph-node5 ~]# sed -n 's/John/JO\lHNNY/p' employee.txt
101,JOhNNY Doe,CEO

 

 

\L 標志

 

 

 


當在 replacement-string 中使用\l 標志時,它會把后面所有的字符都當做小寫字符來處理。
下面的例子,在 replacement-string 中的 H 前面放置了\L 標志,它會把 H 和它后面的所有字符都換成小寫:

[root@ceph-node5 ~]#  sed -n 's/John/JO\LHNNY/p' employee.txt
101,JOhnny Doe,CEO

 

\u 標志


\l 類似,只不過是把字符換成大寫。 當在 replacement-string 中使用\u 標志時,它會把緊跟在其后面的字符當做大寫字符來處理。

下面的例子中, replacement-string 里面的 h 前面有\u 標志,所以 h 將被換成大寫的 H:

[root@ceph-node5 ~]#  sed -n 's/John/jo\uhnny/p' employee.txt
101,joHnny Doe,CEO

 

 

 

 

 

 

\U 標志

 


當在 replacement-string 中使用\U 標志時,它會把后面所有的字符都當做大寫字符來處理。
下面的例子中, replacement-string 里面的 h 前面有\U 標志,所以 h 及其以后的所有字符,都將被換成大寫:

[root@ceph-node5 ~]# sed -n 's/John/jo\Uhnny/p' employee.txt
101,joHNNY Doe,CEO

 

 

 

\E 標志


\E 標志需要和\U \L 一起使用,它將關閉\U \L 的功能。下面的例子將把字符串"Johnny Boy"的每個字符都以大寫的形式打印出來,

因為在 replacement-string 前面使用了\U 標志:

[root@ceph-node5 ~]# sed -n 's/John/\UJohnny Boy/p' employee.txt
101,JOHNNY BOY Doe,CEO

 


下面將把 John 換成 JOHNNY Boy:

[root@ceph-node5 ~]# sed -n 's/John/\UJohnny\E Boy/p' employee.txt
101,JOHNNY Boy Doe,CEO

 

這個例子只把 Johnny 顯示為大寫,因為在 Johnny 后面使用了\E 標志(關閉了\U 的功能)替換標志的用法

上面的例子僅僅展示了這些標志的用法和功能。 然而,如果你使用的是具體的字符串,那么這些選項未必有什么作用,因為你可以在需要的地方寫出精確的字符串,

而不需要使用這些標志進行轉換。和分組配合使用時,這些選項就顯得很有用了。

前面例子中我們已經學會了如何使用分組調換第一列和第三列的位置。 使用上述標志,可以把整個分組轉換為小寫或大寫。
下面的例子,雇員 ID 都顯示為大寫,職位都顯示為小寫:

[root@ceph-node5 ~]# sed 's/\([^,]*\),\([^,]*\),\([^,]*\)/\U\2\E,\1,\L\3/' employee.txt
JOHN DOE,101,ceo
JASON SMITH,102,it manager
RAJ REDDY,103,sysadmin
ANAND RAM,104,developer
JANE MILLER,105,sales manager

 


這個例子中:

\U\2\E 把第二個分組轉換為大寫,然后用\E 關閉轉換
\L\3 把第三個分組轉換為小寫

 

 

 

 

 

 

 

四、簡單正則表達式


從以上案例中我們不難發現,我們編寫的腳本指令需要指定一個地址來決定操作范圍,如果不指定則默認對文件的所有行操作。

 

如:sed   'd'     test.txt              將刪除 test.txt 的所有行,而'2d'
則僅刪除第二行。
1.為 sed 指令確定操作地址:
number              指定輸入文件的唯一行號。
first~step       以 first 開始,並指定操作步長為 step,如 1~2,指定第一行,
第三行,第五行... 為操作地址。
[jacob@localhost ~] #sed   -n   '1~2p'   test.txt              打印文件的奇數行。
DEVICE=eth0
BOOTPROTO=static
NETMASK=255.255.255.0
2~5,則可以指定第二行開始,每 5 行匹配一次操作地址。
$                     匹配文件的最后一行。
/regexp/         //中間包含的是正則表達式,通過正則表達式匹配操作地址。
注: //空的正則表達式,匹配最近一次正則表達式的匹配地址,會在后面使用看出效果。
\cregexpc         匹配擴展正則表達式,c 字符可以使用任意字符替代。
addr1,addr2       匹配從操作地址 1 到操作地址 2 的所有行。
[jacob@localhost ~] #sed   '2,8d'   test.txt              #刪除 28 中間的所有行。
DEVICE=eth0
addr1,+N              匹配地址 1 以及后面的 N 行內容。

 



2.正則表達式概述(對你要找內容的一種描述)

char                     字符本身就匹配字符本身,如/abc/就是定位包含 abc的行。
*                     匹配前面表達式出現了 0 或若干次,如/a*/可以幫你找到a,aa,aaa,... ...等等。
\+                     類似於*,但匹配前面表達式的 1 次或多次,這屬於擴展正則表達式。
\?                     類似於*,但匹配前面表達式的 0 次或 1 次,這屬於擴展正則表達式。
\{i\}                類似於*,但匹配前面表達式的 i 次(i 為整數),如: a\{3\}可以幫你找到 aaa。
\{i,j\}            匹配前面表達式的 i 到 j 次,如 a\{1,2\}可以幫你找到 a 或aa 或 aaa。
\{i,\}              匹配前面表達式至少 i 次。
\( \)                將\( \)內的模式存儲在保留空間。最多可以存儲 9 個獨立子模式,
                     可通過轉義\1 至\9 重復保留空間的內容至此點。
\n                     轉義\1 至\9 重復保留空間的內容至此點。
例:test.txt 的內容為 ssttss
grep '\(ss\)tt\1' test.txt                     \1 表示將 ss 重復在 tt 后面
該 grep 命令等同於grep   ssttss   test.txt       在 test.txt 文件中找 ssttss
.                     (點)匹配任意字符。
^                     匹配行的開始,如^test       將匹配所有以 test 開始的行。
$                     匹配行的結尾,如 test$       將匹配所有以 test 結尾的行。
[]                   匹配括號中的任意單個字符,如 a[nt]       將匹配 an 或at。
[^]                 匹配不包含在[]中的字符,如[^a-z]       將匹配除 a-z以外的字符。
\n                   匹配換行符。
\char              轉義特殊字符,如\*,就是匹配字面意義上的星號。

 

 

行的開頭 ( ^ )

^ 匹配每一行的開頭


顯示以 103 開頭的行:

[root@ceph-node5 ~]# sed -n '/^103/ p' employee.txt
103,Raj Reddy,Sysadmin

 


只有^出現在正則表達式開頭時,它才匹配行的開頭。 所以, ^N 匹配所有以 N 開頭的行。

 


行的結尾
( $ )


$匹配行的結尾。


顯示以字符
r 結尾的行:

[root@ceph-node5 ~]# sed -n '/r$/ p' employee.txt
102,Jason Smith,IT Manager
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 單個字符 ( . )

元字符點 . 匹配除換行符之外的任意單個字符

.匹配單個字符
.. 匹配兩個字符
...匹配三個字符
....以此類推

 

 

匹配 0 次或多次 ( * )


星號*匹配 0 個或多個其前面的字符。如: 1* 匹配 0 個或多個 1
先建立下面文件:

[root@ceph-node5 ~]# cat log.txt
log: input.txt
log:
log: testing resumed
log:
log:output created

 


假設你想查看那些包含 log 並且后面有信息的行, log 和信息之間可能有 0 個或多個空格,同時不想查看那些 log:后面沒有任何信息的行。
顯示包含
log:並且 log 后面有信息的行, log 和信息之間可能有空格:

[root@ceph-node5 ~]#  sed -n '/log: *./p' log.txt
log: input.txt
log: testing resumed
log:output created

 

注意: 上面例子中,后面的點.是必需的,如果沒有, sed 只會打印所有包含 log 的行。


匹配一次或多次
( \+ )


“\+”匹配一次或多次它前面的字符,例如 空格\+ “ \+”匹配至少一個或多個空格。
仍舊使用
log.txt 這個文件來作示例。
顯示包含
log:並且 log:后面有一個或多個空格的所有行:

[root@ceph-node5 ~]# sed -n '/log: \+/ p' log.txt
log: input.txt
log: testing resumed

 



注意:這個例子既沒有匹配只包含 log:的行,也沒有匹配 log:output craeted 這一行,因為 log:后面沒有空格。


零次或一次匹配
( \? )


\?匹配 0 次或一次它前面的字符。 如:

[root@ceph-node5 ~]# sed -n '/log: \+/ p' log.txt
log: input.txt
log: testing resumed
[root@ceph-node5 ~]# sed -n '/log: \?/ p' log.txt
log: input.txt
log:
log: testing resumed
log:
log:output created

 

 

轉義字符 ( \ )

 

 

如果要在正則表達式中搜尋特殊字符(:*,.),必需使用\來轉義它們。

sed -n '/127\.0\.0\.1/ p' /etc/hosts
127.0.0.1 localhost

 

字符集 ( [0-9] )

 

 

字符集匹配方括號中出現的任意一個字符。
匹配包含
23 或者 4 的行:

[root@ceph-node5 ~]# sed -n '/[234]/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


在方括號中,可以使用連接符-指定一個字符范圍。如[0123456789]可以用[0-9]表示,字母可以用[a-z],[A-Z]表示,等等。
匹配包含
23 或者 4 的行(另一種方式):

[root@ceph-node5 ~]# sed -n '/[2-4]/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

 或操作符 ( | )


管道符號|用來匹配兩邊任意一個子表達式。 子表達式 1|子表達式 2 匹配子表達式 1 或者子表達式 2
打印包含 101 或者包含 102 的行:

[root@ceph-node5 ~]# sed -n '/101\|102/ p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


需要注意, | 需要用\轉義。
打印包含數字 2~3 或者包含 105 的行:

[root@ceph-node5 ~]# sed -n '/[2-3]\|105/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 

 

精確匹配 m ( {m} )




正則表達式后面跟上{m}標明精確匹配該正則 m 次。
請先建立如下文件:

[root@ceph-node5 ~]# vim numbers.txt
1
12
123
1234
12345
123456

 


打印包含任意數字的行(這個命令將打印所有行):

[root@ceph-node5 ~]# sed -n '/[0-9]/ p' numbers.txt
1
12
123
1234
12345
123456

 


打印包含 5 個數字的行:

[root@ceph-node5 ~]# sed -n '/^[0-9]\{5\}$/ p' numbers.txt
12345

 


注意這里一定要有開頭和結尾符號,即^$,並且{}都要用\轉義

 

匹配 m n ( {m,n} ):


正則表達式后面跟上{m,n}表明精確匹配該正則至少 m,最多 n 次。 m n 不能是負數,並且要小於 255.
打印由 3 5 個數字組成的行:

[root@ceph-node5 ~]# sed -n '/^[0-9]\{3,5\}$/ p' numbers.txt
123
1234
12345

 



正則表達式后面跟上{m,}表明精確匹配該正則至少 m,最多不限。 (同樣,如果是{,n}表明最多匹配 n 次,最少一次)

字符邊界 ( \b )

\b 用來匹配單詞開頭(\bxx)或結尾(xx\b)的任意字符,因此\bthe\b 將匹配 the,但不匹配 they。\bthe 將匹配 the they.
請先建立如下文件:

[root@ceph-node5 ~]# vim words.txt
word matching using: the
word matching using: thethe
word matching using: they

 

 

匹配包含 the 作為整個單詞的行:

[root@ceph-node5 ~]# sed -n '/\bthe\b/ p' words.txt
word matching using: the

 


注意:如果沒有后面那個\b,將匹配所有行。
匹配所有以 the 開頭的單詞:

[root@ceph-node5 ~]# sed -n '/\bthe/ p' words.txt
word matching using: the
word matching using: thethe
word matching using: they

 

回溯引用 ( \n )

使用回溯引用,可以給正則表達式分組,以便在后面引用它們。
只匹配重復 the 兩次的行:

[root@ceph-node5 ~]# sed -n '/\(the\)\1/ p' words.txt
word matching using: thethe

 


同理, ”\([0-9]\)\1” 匹配連續兩個相同的數字,如 11,22,33 …..

 

sed 替換中使用正則表達式

 

下面是一些使用正則表達式進行替換的例子。
employee.txt 中每行最后兩個字符替換為”,Not Defined”:

[root@ceph-node5 ~]# sed -n 's/..$/,Not Defined/ p' employee.txt
101,John Doe,C,Not Defined
102,Jason Smith,IT Manag,Not Defined
103,Raj Reddy,Sysadm,Not Defined
104,Anand Ram,Develop,Not Defined
105,Jane Miller,Sales Manag,Not Defined

 


刪除以 Manager 開頭的行的后面的所有內容:

[root@ceph-node5 ~]# sed 's/^Manager.*//' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


這個示例好像有點問題啊,我覺得應該是 sed ‘s/^Manager.*/Manager/’
刪除所有以#開頭的行:

[root@ceph-node5 ~]#  sed -e 's/#.*// ; /^$/ d' employee.txt 
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager


我覺得用 sed ‘/^#/ d’ 更好
建立下面的 test.html 文件:

[root@ceph-node5 ~]# vim test.html
<html><body><h1>Hello World!</h1></body></html>

 


清除 test.html 文件中的所有 HTML 標簽:

[root@ceph-node5 ~]# sed 's/<[^>]*>//g' test.html
Hello World!

 


刪除所有注釋行和空行:

[root@ceph-node5 ~]# sed -e 's/#.*// ; /^$/ d' /etc/profile

 


只刪除注釋行,保留空行:

[root@ceph-node5 ~]# sed '/#.*/ d' /etc/profile 

 

使用 sed 可以把 DOS 的換行符(CR/LF)替換為 Unix 格式。 當把 DOS 格式的文件拷到 Unix 上,你會發現,每行結尾都有\r\n .
使用 sed DOS 格式的文件轉換為 Unix 格式:

sed ‘s/.$//’ filename 

 

 

 

五、執行 sed

 

單行內執行多個 sed 命令

 

 

 

1. 使用多命令選項 –e
多命令選項-e 使用方法如下:

sed –e 'command1' –e 'command2' –e 'command3'

 


/etc/passwd 文件中,搜索 rootnobody mail:

[root@ceph-node5 ~]# sed -n -e '/^root/ p' -e '/^nobody/ p' -e '/^mail/ p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 




2. 使用\ 折行執行多個命令


在執行很長的命令,比如使用
-e 選項執行多個 sed 命令時,可以使用\來把命令折到多行

[root@ceph-node5 ~]# sed -n -e '/^root/ p' \
> -e '/^nobody/ p' \
> -e '/^mail/ p' \
> /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 


3. 使用{ }把多個命令組合
如果要執行很多
sed 命令,可以使用{ }把他們組合起來執行,如:

[root@ceph-node5 ~]# sed -n '{
> /^root/ p
> /^nobody/ p
> /^mail/ p
> }' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 

 

 

sed 腳本文件

如果用重復使用一組 sed 命令,那么可以建立 sed 腳本文件,里面包含所有要執行的 sed 命令,然后用-f 選項來使用。
首先建立下面文件,里面包含了所有要執行的
sed 命令。 前面已經解釋過各個命令的含義,
現在你應該知道所有命令的意思了。

[root@ceph-node5 ~]# vim mycommands.sed
s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g
s/^.*/<&>/
s/Developer/IT Manager/
s/Manager/Director/

 

現在執行腳本里面的命令:

[root@ceph-node5 ~]# sed -f mycommands.sed employee.txt
<John Doe,101, CEO>
<Jason Smith,102, IT Director>
<Raj Reddy,103, Sysadmin>
<Anand Ram,104, IT Director>
<Jane Miller,105, Sales Director>

 

sed 注釋

 

sed 注釋以#開頭。因為 sed 是比較晦澀難懂的語言,所以你現在寫下的 sed 命令,時間一長,再看時就不那么容易理解了。

因此,建議把寫腳本時的初衷作為注釋,寫到腳本里面。 如下所示:

[root@ceph-node5 ~]#  vim mycommands.sed
#交換第一列和第二列 s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g #把整行內容放入<>中 s/^.*/<&>/ #把 Developer 替換為 IT Manager s/Developer/IT Manager/ #把 Manager 替換為 Director s/Manager/Director/

 

 


注意: 如果 sed 腳本第一行開始的兩個字符是#n 的話, sed 會自動使用-n 選項(即不自動打印模式空間的內容)

 

 

sed 當做命令解釋器使用

 


一如你可以把命令放進一個 shell 腳本中,然后調用腳本名稱來執行它們一樣,你也可以把sed 用作命令解釋器。

要實現這個功能,需要在 sed 腳本最開始加入"#!/bin/sed –f",如下所示:

[root@ceph-node5 ~]#  vim myscript.sed
#!/bin/sed -f
#交換第一列和第二列
s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g
#把整行內容放入<>中
s/^.*/<&>/
#把 Developer 替換為 IT Manager
s/Developer/IT Manager/
#把 Manager 替換為 Director
s/Manager/Director/

 

現在,給這個腳本加上可執行權限,然后直接在命令行調用它:

[root@ceph-node5 ~]#  chmod u+x myscript.sed
[root@ceph-node5 ~]#  ./myscript.sed employee.txt
<John Doe,101, CEO>
<Jason Smith,102, IT Director>
<Raj Reddy,103, Sysadmin>
<Anand Ram,104, IT Director>
<Jane Miller,105, Sales Director>

 


也可以指定-n 選項來屏蔽默認輸出:

[root@ceph-node5 ~]# vim testscript.sed
#!/bin/sed -nf
/root/ p
/nobody/ p
/mail/ p

 



然后加上可執行權限,執行:

[root@ceph-node5 ~]# chmod u+x testscript.sed
[root@ceph-node5 ~]# ./testscript.sed /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 


處於測試目的,把 testscript.sed 里面的-n 去掉,然后再執行一次, 觀察它是如何運行的。
重要提示
:使用-n 時,必須是-nf.如果你寫成-fn,執行腳本時就會獲得下面的錯誤:

[root@ceph-node5 ~]#  ./testscript.sed /etc/passwd
/bin/sed: couldn't open file n: No such file or directory

 

 

 

直接修改輸入文件

目前為止,你知道 sed 默認不會修改輸入文件, 它只會把輸出打印到標准輸出上。 當想保存結果時,把輸出重定向到文件中(或使用 w 命令)
執行下面的例子之前,先備份一下
employee.txt 文件:

[root@ceph-node5 ~]# cp employee.txt{,.orig}

 


為了修改輸入文件,通常方法是把輸出重定向到一個臨時文件,然后重命名該臨時文件:

[root@ceph-node5 ~]# sed 's/John/Johnny/' employee.txt > new-employee.txt  
[root@ceph-node5 ~]# mv new-employee.txt employee.txt

 


相比這種傳統方法,可以在 sed 命令中使用-i 選項,使 sed 可以直接修改輸入文件:
在原始文件 employee.txt 中,把 John 替換為 Johnny:

[root@ceph-node5 ~]# sed -i 's/John/Johnny/' employee.txt
[root@ceph-node5 ~]# cat employee.txt
101,Johnnyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


再次提醒: -i 會修改輸入文件。 或許這正是你想要的,但是務必小心。 一個保護性的措施是,在-i 后面加上備份擴展,這一 sed 就會在修改原始文件之前,備份一份。
在原始文件
employee.txt 中,把 John 替換為 Johnny,但在替換前備份 employee.txt:

[root@ceph-node5 ~]# sed -ibak 's/John/Johnny/' employee.txt

 


備份的文件如下:

[root@ceph-node5 ~]# cat employee.txtbak
101,Johnnyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


修改后的原始文件為:

[root@ceph-node5 ~]# cat employee.txt
101,Johnnynyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


除了使用-i,也可以使用完整樣式 –in-place.下面兩個命令是等價的:

[root@ceph-node5 ~]# sed -ibak 's/John/Johnny/' employee.txt 
[root@ceph-node5 ~]# sed -in-place=bak 's/John/Johnny/' employee.txt

 



最后,為了繼續下面的例子,把原來的 employee.txt 還原回去:

cp employee.txt.orig employee.txt

 



六、sed 附加命令

 

追加命令(命令 a)

 



使用命令 a 可以在指定位置的后面插入新行。
語法:

sed '[address] a the-line-to-append' input-file

 


在第 2 行后面追加一行(原文這里可能有問題,沒有寫明行號):

[root@ceph-node5 ~]# sed '2 a 203,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


employee.txt 文件結尾追加一行:

[root@ceph-node5 ~]# sed '$ a 106,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
106,Jack Johnson,Engineer

 



在匹配
Jason 的行的后面追加兩行:

[root@ceph-node5 ~]# sed '/Jason/a\
> 203,Jack Johnson,Engineer\
> 204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


追加多行之間可以用\n 來換行,這樣就不用折行了,上面的命令等價於:
sed '/Jason/a 203,Jack Johnson,Engineer\n204,Mark Smith,Sales Engineer' employee.txt

 

 

插入命令(命令 i)


插入命令 insert 命令和追加命令類似,只不過是在指定位置之前插入行。
語法:

 sed '[address] i the-line-to-insert' input-file

 


employee.txt 的第 2 行之前插入一行:

[root@ceph-node5 ~]# sed '2 i 203,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
203,Jack Johnson,Engineer
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


employee.txt 最后一行之前,插入一行:

[root@ceph-node5 ~]# sed '$ i 108,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
108,Jack Johnson,Engineer
105,Jane Miller,Sales Manager

 


sed
也可以插入多行。
在匹配
Jason 的行的前面插入兩行:

sed '/Jason/i\
203,Jack Johnson,Engineer\
204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


修改命令(命令 c)
修改命令 change 可以用新行取代舊行。
語法:

sed '[address] c the-line-to-insert' input-file

 



用新數據取代第 2 :

[root@ceph-node5 ~]# sed '2 c 202,Jack,Johnson,Engineer' employee.txt
101,John Doe,CEO
202,Jack,Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


這里命令 c 等價於替換: $ sed '2s/.*/202,Jack,Johnson,Engineer/' employee.txt
sed 也可以用多行來取代一行。
用兩行新數據取代匹配
Raj 的行:

[root@ceph-node5 ~]# sed '/Raj/c\
> 203,Jack Johnson,Engineer\
> 204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


命令 a、 i 和 c 組合使用


命令 ai c 可以組合使用。下面的例子將完成三個操作:

a 在"Jason"后面追加"Jack Johnson"
i 在"Jason"前面插入"Mark Smith"
c 用"Joe Mason"替代"Jason"

 


如下:

[root@ceph-node5 ~]# sed '/Jason/ {a\
> 204,Jack Johnson,Engineer
> i\
> 202,Mark Smith,Sales Engineer
> c\
> 203,Joe Mason,Sysadmin
> }' employee.txt
101,John Doe,CEO
202,Mark Smith,Sales Engineer
203,Joe Mason,Sysadmin
204,Jack Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

打印不可見字符(命令 l)

 


命令 l 可以打印不可見的字符,比如制表符\t,行尾標志$等。
請先建立下面文件以便用於后續測試,請確保字段之間使用制表符
(Tab )分開:

[root@ceph-node5 ~]# vim  tabfile.txt
fname First Name
lname Last Name
mname Middle Name

 

 

使用命令 l,把制表符顯示為\t,行尾標志顯示為 EOL:

[root@ceph-node5 ~]# sed -n 'l' tabfile.txt
fname First Name$
lname Last Name$
mname Middle Name$

 


如果在 l 后面指定了數字,那么會在第 n 個字符處使用一個不可見自動折行,效果如下:

[root@ceph-node5 ~]# sed -n 'l 20' employee.txt
101,John Doe,CEO$
102,Jason Smith,IT \
Manager$
103,Raj Reddy,Sysad\
min$
104,Anand Ram,Devel\
oper$
105,Jane Miller,Sal\
es Manager$

 


這個功能只有 GNU sed 才有。

 

打印行號(命令=)


命令=會在每一行后面顯示該行的行號。

打印所有行號:

[root@ceph-node5 ~]#  sed '=' employee.txt
1
101,John Doe,CEO
2
102,Jason Smith,IT Manager
3
103,Raj Reddy,Sysadmin
4
104,Anand Ram,Developer
5
105,Jane Miller,Sales Manager

 


提示: 把命令=和命令 N 配合使用,可以把行號和內容顯示在同一行上(下文解釋)
只打印
1,2,3 行的行號:

[root@ceph-node5 ~]# sed '1,3 =' employee.txt
1
101,John Doe,CEO
2
102,Jason Smith,IT Manager
3
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


打印包含關鍵字”Jane”的行的行號,同時打印輸入文件中的內容:

[root@ceph-node5 ~]#  sed '/Jane/ =' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
5
105,Jane Miller,Sales Manager

 


如果你想只顯示行號但不顯示行的內容,那么使用-n 選項來配合命令=:

[root@ceph-node5 ~]# sed -n '/Raj/ =' employee.txt
3

 


打印文件的總行數:

[root@ceph-node5 ~]# sed -n '$ =' employee.txt
5

 

 

轉換字符(命令 y)

 


命令 y 根據對應位置轉換字符。好處之一便是把大寫字符轉換為小寫,反之亦然。
下面例子中,將把
a 換為 Ab 換為 Bc 換為 C,以此類推:

[root@ceph-node5 ~]#  sed 'y/abcde/ABCDE/' employee.txt
101,John DoE,CEO
102,JAson Smith,IT MAnAgEr
103,RAj REDDy,SysADmin
104,AnAnD RAm,DEvElopEr
105,JAnE MillEr,SAlEs MAnAgEr

 


把所有小寫字符轉換為大寫字符:

[root@ceph-node5 ~]# sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' employee.txt
101,JOHN DOE,CEO
102,JASON SMITH,IT MANAGER
103,RAJ REDDY,SYSADMIN
104,ANAND RAM,DEVELOPER
105,JANE MILLER,SALES MANAGER

 

操作多個文件

 


之前的例子都只操作了單個文件, sed 也可以同時處理多個文件。
/etc/passwd 中搜索 root 並打印出來:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

 


/etc/group 中搜索 root 並打印出來:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/group
root:x:0:

 


同時在/etc/passwd /etc/group 中搜索 root:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/passwd /etc/group
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
root:x:0:

 


 退出 sed(命令 q)


命令 q 終止正在執行的命令並退出 sed
之前提到,正常的
sed 執行流程是:讀取數據、執行命令、打印結果、重復循環。
sed 遇到 q 命令,便立刻退出,當前循環中的后續命令不會被執行,也不會繼續循環。
打印第
1 行后退出:

[root@ceph-node5 ~]# sed 'q' employee.txt
101,John Doe,CEO

 


打印第 5 行后退出,即只打印前 5 行:

 

[root@ceph-node5 ~]# sed '5 q' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 



打印所有行,直到遇到包含關鍵字 Manager 的行:

[root@ceph-node5 ~]# sed '/Manager/ q' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


注意: q 命令不能指定地址范圍(或模式范圍),只能用於單個地址(或單個模式)

 

 

從文件讀取數據(命令 r)

在處理輸入文件是,命令 r 會從另外一個文件讀取內容,並在指定的位置打印出來。

下面的例子將讀取 log.txt 的內容,並在打印 employee.txt 最后一行之后,把讀取的內容打印出來。事實上它把 employee.txt log.txt 合並然后打印出來。

[root@ceph-node5 ~]# sed '$ r log.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
log: input.txt
log:
log: testing resumed
log:
log:output created

 


也可以給命令 r 指定一個模式。下面的例子將讀取 log.txt 的內容,並且在匹配’Raj’的行后面打印出來:

[root@ceph-node5 ~]# sed '/Raj/ r log.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
log: input.txt
log:
log: testing resumed
log:
log:output created
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

sed 模擬 Unix 命令(cat,grep,read)

 

 之前的例子的完成的功能都很像標准的 Unix 命令。使用 sed 可以模擬很多 Unix 命令。完成它們,以便更好地理解 sed 的工作過程

 

模擬 cat 命令

 

cat employee.txt

 


下面的每個 sed 命令的輸出都和上面的 cat 命令的輸出一樣:

sed 's/JUNK/&/p' employee.txt 
sed -n 'p' employee.txt
sed 'n' employee.txt
sed 'N' employee.txt

 

 

 

模擬 grep 命令



 

grep Jane employee.txt

 


下面的每個 sed 命令的輸出都和上面的 grep 命令的輸出一樣:

sed -n 's/Jane/&/ p' employee.txt 
sed -n '/Jane/ p' employee.txt

 



grep –v (打印不匹配的行):

sed -n '/Jane/ !p' employee.txt 

 


注意: 這里不能使用 sed –n 's/Jane/&/ !p' 了。



模擬 head 命令

 

head -10 /etc/passwd

 


下面的每個 sed 命令的輸出都和上面的 head 命令的輸出一樣:

sed '11,$ d' /etc/passwd 
sed -n '1,10 p' /etc/passwd
sed '10 q' /etc/passwd

 

 

 

sed 命令選項



-n 選項

 


之前已經討論過多次,並且在很多例子中也使用到了-n 選項。 該選項屏蔽 sed 的默認輸出。
也可以使用—quiet,或者—silent 來代替-n,它們的作用是相同的。
下面所有命令都是等價的:

sed -n 'p' employee.txt 
sed --quiet 'p' employee.txt

 

 

-f 選項

 


可以把多個 sed 命令保存在 sed 腳本文件中,然后使用-f 選項來調用,這點之前已經演示過
了。你也可以使用--
file 來代替-f
下面的命令是等價的:

sed -n -f test-script.sed /etc/passwd  
sed -n -file=test-script.sed /etc/passwd

 

 

-e 選項

 


該選項執行來自命令行的一個
sed 命令,可以使用多個-e 來執行多個命令。也可以使用-expression 來代替。
下面所有命令都是等價的
:

sed -n -e '/root/ p' /etc/passwd  
sed -n --expression '/root/ p' /etc/passwd

 



-i 選項

下面所有命令都是等價的:
我們已經提到, sed 不會修改輸入文件,只會把內容打印到標准輸出,或則使用 w 命令把內容寫到不同的文件中。 我們也展示了如何使用-i 選項來直接修改輸入文件。
在原始文件
employee.txt 中,用 Johnny 替換 John:

sed -i 's/John/Johnny/' employee.txt 

 


執行和上面相同的命令,但在修改前備份原始文件:

sed -ibak 's/John/Johnny/' employee.txt 

 


也可以使用--in-place 來代替-i:
下面的命令是等價的:

 sed -ibak 's/John/Johnny/' employee.txt
sed --in-place=bak 's/John/Johnny/' employee.txt

 



 

-c 選項

該選項應和-i 配合使用。使用-i 時,通常在命令執行完成后, sed 使用臨時文件來保持更改后的內容,然后把該臨時文件重命名為輸入文件。

但這樣會改變文件的所有者(奇怪的是我的測試結果是它不會改變文件所有者),配合 c 選項,可以保持文件所有者不變。也可以使用--copy 來代替。
下面的命令是等價的
:

sed -ibak -c 's/John/Johnny/' employee.txt 
sed --in-place=bak --copy 's/John/Johnny/' employee.txt

 



-l 選項

 


指定行的長度,需要和
l 命令配合使用(注意選項 l 和命令 l,不要弄混了,上面提到的命令 i
和選項 i 也不要搞錯)使用-l 選項即指定行的長度。也可以使用--line-length 來代替.
下面的命令是等價的:

sed -n -l 20 'l' employee.txt

 



請注意,下面的例子不使用-l(原文是-n,應該是弄錯)選項,同樣可以獲取相同的輸出:

sed -n 'l 20' employee.txt --posix option  

 

 

打印模式空間(命令 n)

 


命令 n 打印當前模式空間的內容,然后從輸入文件中讀取下一行。 如果在命令執行過程中遇到 n,那么它會改變正常的執行流程。
打印每一行在模式空間中的內容:

sed n employee.txt

 


如果使用了-n 選項,將沒有任何輸出:

 sed -n n employee.txt

 


再次提醒:不要把選項-n 和命令 n 弄混了)
前面提到, sed 正常流程是讀取數據、執行命令、打印輸出、重復循環。
命令
n 可以改變這個流程,它打印當前模式空間的內容,然后清除模式空間,讀取下一行進
來,然后繼續執行后面的命令。
假定命令
n 前后各有兩個其他命令,如下:

sed-command-1
sed-command-2
n
sed-command-3
sed-command-4

 


這種情況下, sed-command-1 sed-command-2 會在當前模式空間中執行,然后遇到 n,它打印當前模式空間的內容,並清空模式空間,讀取下一行,

然后把 sed-command-3 sed-command-4 應用於新的模式空間的內容。
提示:上面的例子中可以看到,命令
n 本身沒有多大用處,然而當它和保持空間的命令配合使用時,就很強大了,后面會詳細解釋。

 

七、保持空間和模式空間命令

 

 

SED之所以能以行為單位的編輯或修改文本,其原因在於它使用了兩個空間:一個是活動的模式空間(pattern space),另一個是起輔助作用的暫存緩沖區(holdingspace)這2個空間的使用。

模式空間:如你所知,模式空間用於 sed 執行的正常流程中。 該空間 sed 內置的一個緩沖區,用來存放、修改從輸入文件讀取的內容。

 保持空間: 保持空間是另外一個緩沖區,用來存放臨時數據。 Sed 可以在保持空間和模式空間交換數據,但是不能在保持空間上執行普通的 sed 命令。 我們已經討論
過,每次循環讀取數據過程中,模式空間的內容都會被清空,然而保持空間的內容則保持不變,不會在循環中被刪除。

 

sed編輯器逐行處理文件,並將輸出結果打印到屏幕上。sed命令將當前處理的行讀入模式空間(pattern space)進行處理,sed在該行上執行完所有命令后就將處理好的行打印到屏幕上(除非之前的命令刪除了該行),sed處理完一行就將其從模式空間中刪除,然后將下一行讀入模式空間,進行處理、顯示。處理完文件的最后一行,sed便結束運行。sed在臨時緩沖區(模式空間)對文件進行處理,所以不會修改原文件,除非顯示指明-i選項。

模式空間:可以想成工程里面的流水線,數據之間在它上面進行處理,用於處理文本行。

保持空間:可以想象成倉庫,我們在進行數據處理的時候,作為數據的暫存區域,用於保留文本行,是保存已經處理過的輸入行,默認有一個空行。

 

與模式空間和暫存空間(hold space)相關的命令:

 

n 輸出模式空間行,讀取下一行替換當前模式空間的行,執行下一條處理命令而非第一條命令。

N 讀入下一行,追加到模式空間行后面,此時模式空間有兩行。

h 把模式空間的內容復制到保留空間,覆蓋模式

H 把模式空間的內容追加到保留空間,追加模式

g 把保留空間的內容復制到模式空間,覆蓋模式

G 把保留空間的內容追加到模式空間,追加模式

x 將暫存空間的內容於模式空間里的當前行互換。

! 對所選行以外的所有行應用命令。

注意:暫存空間里默認存儲一個空行。



 

 

 

 

請先建立如下文件,以用於保持空間的示例:

[root@ceph-node5 ~]# vim empnametitle.txt
John
Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager


可以看到,在這個文件中,每個雇員的名稱和職位位於連續的兩行內。

 

用保持空間替換模式空間(命令 x)

 

命令 x(Exchange)交換模式空間和保持空間的內容。 該命令本身沒有多大用處,但如果和其他命令配合使用,則非常強大了。
假定目前模式空間內容為"
line 1",保持空間內容為"line 2"。那么執行命令 x 后,模式空間的內容變為”line 2”,保持空間的內容變為"line 1"

 

 

把模式空間的內容復制到保持空間(命令 h)

 


命令 h(hold)把模式空間的內容復制到保持空間,和命令 x 不同,命令 h 不會修改當前模式空間的內容。 執行命令 h 時,當前保持空間的內容會被模式空間的內容覆蓋。假定目前模式空間內容為"line 1",保持空間內容為"line 2"。那么執行命令 h 后,模式空間的內容仍然為"line 1",保持空間的內容則變為"line 1"
打印管理者的名稱
:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{x;p}' empnametitle.txt
Jason Smith
Jane Miller

 

上面例子中:

/Manager/!h –如果模式空間內容不包含關鍵字’Manager’(模式后面的!表示不匹配該模式), 那么復制模式空間內容到保持空間。 (這樣一來,保持空間的內容可能會
是雇員名稱或職位,而不是’Manager’.)注意, 和前面的例子不同,這個例子中沒有使用命令 n 來獲取下一行,而是通過正常的流程來讀取后續內容。


/Manager/{x;p} –如果模式空間內容包含關鍵字’Manager’,那么交換保持空間和模式空間的內容,並打印模式空間的內容。 這和我們在命令 x 的示例中的用法是相同的

 

你也可以把上面命令保存到 sed 腳本中執行:

[root@ceph-node5 ~]# vi h.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{x;p}

[root@ceph-node5 ~]#  chmod u+x h.sed
[root@ceph-node5 ~]# ./h.sed empnametitle.txt
Jason Smith
Jane Miller

 

 

把模式空間內容追加到保持空間(命令 H)

大寫 H 命令表示把模式空間的內容追加到保持空間,追加之前保持空間的內容不會被覆蓋;
相反, 它在當前保持空間內容后面加上換行符
\n,然后把模式空間內容追加進來。
假定目前模式空間內容為
”line 1”,保持空間內容為”line 2”。那么執行命令 H 后,模式空間的內容沒有改變, 仍然為”line 1”,保持空間的內容則變為”line2\nline 1”
打印管理者的名稱和職位
(在不同的行上):

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{H;x;p}' empnametitle.txt
Jason Smith
IT Manager
Jane Miller
Sales Manager

 


上面例子中:

/Manager/!h –如果模式空間內容不包含關鍵字’Manager’(模式后面的!表示不匹配該模式),那么復制模式空間內容到保持空間。 (這樣一來,保持空間的內容可能會
是雇員名稱或職位,而不是’Manager’.)。這和之前使用命令 h 的方法是一樣的。


/Manager/{H;x;p} –如果模式空間內容包含關鍵字’Manager’,那么命令 H 把模式空間的內容(也就是管理者的職位)作為新行追加到保持空間,所以保持空間內容會變 為”雇員名稱\n 職位”(職位包含關鍵字 Manager)。 然后命令 x 交換模式空間和保持空間的內容,隨后命令 p 打印模式空間的內容。

 

你也可以把上面命令保存到 sed 腳本中執行:

[root@ceph-node5 ~]# vi H-upper.sed 
#!/bin/sed -nf
/Manager/!h
/Manager/{H;x;p}
[root@ceph-node5 ~]# chmod u+x H-upper.sed
[root@ceph-node5 ~]# ./H-upper.sed empnametitle.txt
Jason Smith
IT Manager
Jane Miller
Sales Manager

 


如果想把雇員名稱和職位顯示在同一行,以分號分開,那么只需稍微修改一下即可,如下:

[root@ceph-node5 ~]#  sed -n -e '/Manager/!h' -e '/Manager/{H;x;;s/\n/:/p}' empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 


這個例子除了在第二個-e 后面的命令中加入了替換命令之外,和前面的例子一樣。 Hx p 都完成和之前相同的操作;在交換模式空間和保持空間之后,命令 s 把換行符\n 替換為分號,然后打印出來。
你也可以把上面命令保存到
sed 腳本中執行:

[root@ceph-node5 ~]# vi H1-upper.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{H;x;s/\n/:/;p}
[root@ceph-node5 ~]# chmod u+x H1-upper.sed
[root@ceph-node5 ~]# ./H1-upper.sed empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 

 

 

把保持空間內容復制到模式空間(命令 g)

 

命令 g(get)把保持空間的內容復制到模式空間。
這樣理解:命令 h 保持(hold)住保持空間(hold space),命令 g 從保持空間獲取(get)內容。
假定當前模式空間內容為”line 1”,保持空間內容為”line 2”;執行命令 g 之后,模式空間內容變為”line 2”,保持空間內容仍然為”line 2”

打印管理者的名稱:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{g;p}' empnametitle.txt
Jason Smith
Jane Miller

 




上面例子中:

/Manager/!h –最近幾個例子都在用它。如果模式空間內容不包含關鍵字’Manager’,那么就把他復制到保持空間。
/Manager/{g;p} –把保持空間的內容丟到模式空間中,然后打印出來

 

你也可以把上面命令保存到 sed 腳本中執行:

[root@ceph-node5 ~]# vi g.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{g;p}
[root@ceph-node5 ~]# chmod u+x g.sed
[root@ceph-node5 ~]# ./g.sed empnametitle.txt
Jason Smith
Jane Miller

 

把保持空間追加到模式空間(命令 G)


大寫 G 命令把當前保持空間的內容作為新行追加到模式空間中。模式空間的內容不會被覆蓋,該命令在模式空間后面加上換行符\n,然后把保持空間內容追加進去。
G g 的用法類似於 H h;小寫命令替換原來的內容,大寫命令追加原來的內容。
假定當前模式空間內容為
”line 1”,保持空間內容為”line 2”;命令 G 執行后,模式空間內容變為”line 1\nline 2”,同時保持空間內容不變,仍然為”line 2”
以分號分隔,打印管理者的名稱和職位
:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{x;G;s/\n/:/;p}' empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager


上面例子中:

 

/Manager/!h –和前面的例子一樣,如果模式空間內容不包含關鍵字’Manager’,那么就把他復制到保持空間。
/Manager/{x;G;s/\n/:/;p} –如果模式空間包含’Manager’,那么:
  x –交換模式空間和保持空間的內容。
  G –把保持空間的內容追加到模式空間。
  s/\n/:/ --在模式空間中,把換行符替換為分號:。
  p 打印模式空間內容
  注意: 如果舍去命令 x,即使用/Manager/{G;s/\n/:/;p},那么結果會由“雇員職位: 雇員名稱”變成”雇員名稱: 雇員職位

 

也可把上述命令寫到 sed 腳本中然后執行:

[root@ceph-node5 ~]# vi G-upper.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{x;G;s/\n/:/;p}
[root@ceph-node5 ~]# ./G-upper.sed empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 

 

八、sed 多行模式及循環

 
 Sed 默認每次只處理一行數據,除非使用 H,G 或者 N 等命令創建多行模式,每行之間用換行符分開。
本章將解釋適用於多行模式的
sed 命令。
提示:在處理多行模式是,請務必牢記
^只匹配該模式的開頭,即最開始一行的開頭,且$只匹配該模式的結尾,即最后一行的結尾。

讀取下一行數據並附加到模式空間(命令 N)

就像大寫的命令 H G 一樣,只會追加內容而不是替換內容,命令 N 從輸入文件中讀取下一行並追加到模式空間,而不是替換模式空間。
前面提到過,小寫命令
n 打印當前模式空間的內容, 並清空模式空間,從輸入文件中讀取下一行到模式空間,然后繼續執行后面的命令。
大寫命令
N,不會打印模式空間內容,也不會清除模式空間內容,而是在當前模式空間內容后加上換行符\n,並且從輸入文件中讀取下一行數據,

追加到模式空間中,然后繼續執行后面的命令。
以分號分隔,打印雇員名稱和職位
:

[root@ceph-node5 ~]# sed -e '{N;s/\n/:/}' empnametitle.txt
John:Doe
CEO:Jason Smith
IT Manager:Raj Reddy
Sysadmin:Anand Ram
Developer:Jane Miller
Sales Manager

 



這個例子中:

N 追加換行符\n 到當前模式空間(雇員名稱)的最后,然后從輸入文件讀取下一行數據,追加進來。因此,當前模式空間內容變為”雇員名稱\n 雇員職位”。
s/\n/:/ 把換行符\n 替換為分號,把分號作為雇員名稱和雇員職位的分隔符

流程如下圖所示:

 

 

 

 

 

打印多行模式中的第一行(命令 P)

目前為止,我們已經學會了三個大寫的命令(H,N,G),每個命令都是追加內容而不是替換內容。
現在我們來看看大寫的
D P,雖然他們的功能和小寫的 d p 非常相似,但他們在多行模式中有特殊的功能。
之前說到,小寫的命令
p 打印模式空間的內容。大寫的 P 也打印模式空間內容,直到它遇到換行符\n

 

 

下面的例子將打印所有管理者的名稱:

( cat << EOF
John
Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager
EOF
) > empname.txt
[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e'/Manager/{x;p}' empname.txt
Jason Smith
Jane Miller

 

 

 

 

 

 

刪除多行模式中的第一行(命令 D)

之前提到,小寫命令 d 會刪除模式空間內容,然后讀取下一條記錄到模式空間,並忽略后面的命令,從頭開始下一次循環。
大寫命令
D,既不會讀取下一條記錄,也不會完全清空模式空間(除非模式空間內只有一行)
它只會:

刪除模式空間的部分內容,直到遇到換行符\n
忽略后續命令,在當前模式空間中從頭開始執行命令

 

 

 

假設有下面文件,每個雇員的職位都用@包含起來作為注釋。 需要注意的是,有些注釋是跨
行的。 如
@Information Technology officer@就跨了兩行。請先建立下面示例文件:

[root@ceph-node5 ~]# vim empnametitle-with-commnet.txt
John Doe
CEO @Chief Executive Officer@
Jason Smith
IT Manager @Infromation Technology
Officer@
Raj Reddy
Sysadmin @System Administrator@
Anand Ram
Developer @Senior
Programmer@
Jane Miller
Sales Manager @Sales
Manager@

 


現在我們的目標是,去掉文件里的注釋:

[root@ceph-node5 ~]#  sed -e '/@/{N;/@.*@/{s/@.*@//;P;D}}' empnametitle-with-commnet.txt
John Doe
CEO 
Jason Smith
IT Manager 
Raj Reddy
Sysadmin 
Anand Ram
Developer 
Jane Miller
Sales Manager 

 

也可把上述命令寫到 sed 腳本中然后執行:

[root@ceph-node5 ~]# vim D-upper.sed
#!/bin/sed -f
/@/{
N
/@.*@/{s/@.*@//;P;D}
}
[root@ceph-node5 ~]# ./D-upper.sed empnametitle-with-commnet.txt
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager

 


這個例子中:

 

/@/{ 這是外傳循環。 Sed 搜索包含@符號的任意行,如果找到,就執行后面的命令;如果沒有找到,則讀取下一行。 為了便於說明,以第 4 行,即”@Information
Technology”(這條注釋跨了兩行)為例,它包含一個@符合,所以后面的命令會被執行。
N 從輸入文件讀取下一行,並追加到模式空間,以上面提到的那行數據為例,這里 N 會讀取第 5 行,即”Officer@”並追加到模式空間,因此模式空間內容變
為”@Informatioin Technology\nOfficer@”。
/@.*@/ 在模式空間中搜索匹配/@.*@/的模式,即以@開頭和結尾的任何內容。當前模式空間的內容匹配這個模式,因此將繼續執行后面的命令。
s/@.*@//;P;D 這個替換命令把整個@Information Technology\nOfficer@”替換為空(相當於刪除)。 P 打印模式空間中的第一行,然后 D 刪除模式空間中的第一行,然
后從頭開始執行命令(即不讀取下一條記錄,又返回到/@/處執行命令)

 

循環和分支(命令 b :label 標簽)

使用標簽和分支命令 b,可以改變 sed 的執行流程:

:label 定義一個標簽
b lable 執行該標簽后面的命令。 Sed 會跳轉到該標簽,然后執行后面的命令。
注意:命令 b 后面可以不跟任何標簽,這種情況下,它會直接跳到 sed 腳本的結尾

下面例子將把 empnametitle.txt 文件中的雇員名稱和職位合並到一行內,字段之間以分號:
分隔,並且在管理者的名稱前面加上一個星號
*

[root@ceph-node5 ~]# vim label.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
/Manager/!b end
s/^/*/
:end
p

 


這個腳本中,鑒於之前的例子,你已經知道 h;n;H;x s/\n/:/的作用了。下面是關於分支的操作

Manager/!b end 如果行內不包含關鍵字”Manager”,則 跳轉到’end’標簽,請注意,
你可以任意設置你想要的標簽名稱。 因此,只有匹配 Manager 的雇員名稱簽名,才會執行 s/^/*/(在行首加上星號*)。
:end 即是標簽

 

給這個腳本加上可執行權限,然后執行:

$ chmod u+x label.sed
$ ./label.sed empnametitle.txt
John Doe:CEO
*Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
*Jane Miller:Sales Manager

 


個人覺得腳本里面的 h;n;H;x 可以用一個 N 替代, 這樣就不用使用保持空間了。
如果不使用標簽,還可以: sed 'N;s/\n/:/;/Manager/s/^/\.*/' empnametitle.txt

 

注意:我這里顯示的結果:

[root@ceph-node5 ~]#  chmod u+x label.sed
[root@ceph-node5 ~]# ./label.sed empnametitle.txt
John:Doe
CEO:Jason Smith
*IT Manager:Raj Reddy
Sysadmin:Anand Ram
Developer:Jane Miller

 

 

使用命令 t 進行循環


命令 t 的作用是,如果前面的命令執行成功,那么就跳轉到 t 指定的標簽處,繼續往下執行后續命令。 否則,仍然繼續正常的執行流程。
下面例子將把 empnametitle.txt 文件中的雇員名稱和職位合並到一行內,字段之間以分號:分隔,並且在管理者的名稱前面加上三個星號*
提示:我們只需把前面例子中的替換命令改為 s/^/***/即可帶到該目的,下面這個例子僅僅是為了解釋命令 t 是如何運行的。

$ vi lable-t.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
: repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
p
$ chmod u+x lable-t.sed
$ ./lable-t.sed empnametitle.txt
John Doe:CEO
***Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
***Jane Miller:Sales Manager
這個例子中:

 

這個例子中:

下面的代碼執行循環
:repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
/Manager/s/^/*/ 如果匹配到 Manager,在行首加上星號*
/\*\*\*/!t repeat 如果沒有匹配到三個連續的星號*(用/\*\*\*/!來表示),並且前面
一行的替換命令成功執行了,則跳轉到名為 repeat 的標簽處(即 t repeat)
:repeat 標簽

 

 

 

九·、綜合案例(案例摘自 GNU sed 官網)

綜合案例 1:重命名文件名為小寫

[jacob@localhost ~] #cat   sed.sh
#! /bin/sh
# rename files to lower/upper case...
#
# usage:
#       move-to-lower *
#       move-to-upper *
# or
#       move-to-lower -R .
#       move-to-upper -R .
#
help()
{
cat << eof
Usage: $0 [-n] [-r] [-h] files...
-n           do nothing, only see what would be done
-R           recursive (use find)
-h           this message
files     files to remap to lower case
Examples:
$0 -n *               (see if everything is ok, then...)
$0 *
$0 -R .
eof
}
apply_cmd='sh'
finder='echo "$@" | tr " " "\n"'
files_only=
while :
do
case "$1" in
-n) apply_cmd='cat' ;;
-R) finder='find "$@" -type f';;
-h) help ; exit 1 ;;
*) break ;;
esac
shift
done
if [ -z "$1" ]; then
echo Usage: $0 [-h] [-n] [-r] files...
exit 1
fi
LOWER='abcdefghijklmnopqrstuvwxyz'
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
case `basename $0` in
*upper*) TO=$UPPER; FROM=$LOWER ;;
*)             FROM=$UPPER; TO=$LOWER ;;
esac
eval $finder | sed -n '
# remove all trailing slashes
s/\/*$//
# add ./ if there is no path, only a filename
/\//! s/^/.\//
# save path+filename
h
# remove path
s/.*\///
# do conversion only on filename
y/'$FROM'/'$TO'/
# now line contains original path+file, while
# hold space contains the new filename
x
# add converted file name to line, which now contains
# path/file-name\nconverted-file-name
G
# check if converted file name is equal to original file name,
# if it is, do not print nothing
/^.*\/\(.*\)\n\1/b
# now, transform path/fromfile\n, into
# mv path/fromfile path/tofile and print it
s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
' | $apply_cmd
綜合案例 2:獲取 bash 環境變量
#!/bin/sh
set | sed -n '
:x
# if no occurrence of ‘=()’ print and load next line
/=()/! { p; b; }
/ () $/! { p; b; }
# possible start of functions section
# save the line in case this is a var like FOO="() "
h
# if the next line has a brace, we quit because
# nothing comes after functions
n
/^{/ q
# print the old line
x; p
# work on the new line now
x; bx

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 sed文件塊處理

 

文件塊處理動作

 

操作符    用途            指令示例
i      行前插入文本    2iYY 在第2行之前添加文本行”YY”
                 4,7iYY 在第4-7行的每一行前添加文本行
a    行后插入文本       2aYY 在第2行之后添加文本
                 /^XX/aYY 在以XX開頭的行之后添加文本
c    替換當前行      2cYY 將第2行的內容修改為”YY”

 

sed逐字符替換

操作y操作可實現逐個字符替換

 

 

修改后的文本有多行時

 

 

以換行符\n分隔
或者,以\強制換行

 

 

 

 

 sed行替換的應用

 

 

 

 sed行替換的應用:

找到主機名配置中的HOSTNAME行

整行替換為新的主機名配置

 

 

 

 

 

 

 

 

 

[root@localhost shell]# grep ^HOSTNAME /etc/sysconfig/network
HOSTNAME=localhost.localdomain              //修改前
[root@localhost shell]# sed -i '/HOSTNAME/cHOSTNAME=mysvr.tarena.com' /etc/sysconfig/network         //整行替換操作
[root@localhost shell]# grep ^HOSTNAME                         /etc/sysconfig/networkHOSTNAME=mysvr.tarena.com         //修改后

 

 

 

 

 

 

 

 

sed導入導出

 

 

導入導出動作

 

r動作應結合-i選項才會存入,否則只輸出
w 動作以覆蓋的方式另存為新文件

 

例子:

操作符      用途        指令示例
r      讀入其他文件      3r b.txt 在第3行下方插入文件b.txt
                   4,7r b.txt 在第4-7每一行后插入文件b.txt
w      寫入其他文件      3w c.txt 將第3行另存為文件c.txt
                   4,7w c.txt 將第4-7行另存為文件c.txt

 

sed復制剪切

 

模式空間

 

存放當前處理的行,將處理結果輸出
若當前行不符合處理條件,則原樣輸出
處理完當前行再讀入下一行來處理

 

 保持空間

 

 

作用類似於'剪切板'
默認存放一個空行(換行符\n)

 

主要處理動作

 

復制到剪貼板:

 


H    模式空間 ---[追加]--->保持空間
h    模式空間 ---[覆蓋]--->保持空間

 

 

 

讀取剪貼板內容並粘貼:

 

G    保持空間 ---[追加]--->模式空間
g    保持空間 ---[覆蓋]--->模式空間

 

 

 

 

 

 

 

 sed 的工作過程:

 

把要處理的行先讀入自己模式空間,然后用處理動作處理,處理完后輸出處理后的結果,並把源數據輸出,然后讀入下一行到模式空間進行處理
* 要處理的數據必須在模式空間,且模式空間不存儲數據
保持空間 保持空間里默認只保存一個換行符號\n
要想存數據放到保持空間里,要手動把數據存進來了才可以;
 保持空間只負責存儲數據,不會輸出數據
要想保持空間里的數據被sed處理,必須手動把保存空間里的數據調入模式空間
* 保持空間只負責存儲數據 放在保持空間里的數據不會被輸出也不會被處理。保持空間里默認只保存一個換行符號\n

 

 

sed流控制 

 

參數選項      注釋
!取反操作    根據定址條件取反
n讀下一行    讀入下一行進行處理(產生隔行的效果)

 

 

 

 

 

 

 

 

 

sed練習:

1、刪除/etc/grub.conf文件中行首的空白符;
sed -r 's@^[[:spapce:]]+@@g' /etc/grub.conf
2、替換/etc/inittab文件中"id:3:initdefault:"一行中的數字為5;
sed 's@\(id:\)[0-9]\(:initdefault:\)@\15\2@g' /etc/inittab
3、刪除/etc/inittab文件中的空白行;
sed '/^$/d' /etc/inittab
4、刪除/etc/inittab文件中開頭的#號;
sed 's@^#@@g' /etc/inittab
5、刪除某文件中開頭的#號及后面的空白字符,但要求#號后面必須有空白字符;
sed -r 's@^#[[:space:]]+@@g' /etc/inittab
6、刪除某文件中以空白字符后面跟#類的行中的開頭的空白字符及#
sed -r 's@^[[:space:]]+#@@g' /etc/inittab
7、取出一個文件路徑的目錄名稱;
echo "/etc/rc.d/" | sed -r 's@^(/.*/)[^/]+/?@\1@g'    
基名:
echo "/etc/rc.d/" | sed -r 's@^/.*/([^/]+)/?@\1@g'    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM