本章大綱:
<a href="https://s3.51cto.com/wyfs02/M00/8C/E6/wKioL1h9oq_iOUcmAAA3mYNzK7I316.png" target="_blank"></a>
7.2 sed
流編輯器,過濾和替換文本。
工作原理:sed指令将目前處理的行讀入模式空間進行處理,處理完把結果輸出,并清空模式空間。然後再将下一行讀入模式空間進行處理輸出,以此類推,直到最後一行。還有一個空間叫保持空間,又稱暫存空間,可以暫時存放一些處理的資料,但不能直接輸出,隻能放到模式空間輸出。
這兩個空間其實就是在記憶體中初始化的一個記憶體區域,存放正在處理的資料和臨時存放的資料。
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
sed [選項] '位址 指令' file
選項
描述
-n
不列印模式空間
-e
執行腳本、表達式來處理
-f
腳本檔案的内容添加到指令被執行
-i
修改原檔案
-r
使用擴充正規表達式
指令
s/regexp/replacement/
替換字元串
p
列印目前模式空間
P
列印模式空間的第一行
d
删除模式空間,開始下一個循環
D
删除模式空間的第一行,開始下一個循環
=
列印目前行号
a \text
目前行追加文本
i \text
目前行上面插入文本
c \text
所選行替換新文本
q
立即退出sed腳本
r
追加文本來自檔案
: label
label為b和t指令
b label
分支到腳本中帶有标簽的位置,如果分支不存在則分支到腳本的末尾
t label
如果s///是一個成功的替換,才跳轉到标簽
h H
複制/追加模式空間到保持空間
g G
複制/追加保持空間到模式空間
x
交換模式空間和保持空間内容
l
列印模式空間的行,并顯示控制字元$
n N
讀取/追加下一行輸入到模式空間
w filename
寫入目前模式空間到檔案
!
取反、否定
&
引用已比對字元串
位址
first~step
步長,每step行,從第first開始
$
比對最後一行
/regexp/
正規表達式比對行
number
隻比對指定行
addr1,addr2
開始比對addr1行開始,直接addr2行結束
addr1,+N
從addr1行開始,向後的N行
addr1,~N
從addr1行開始,到N行結束
部落格位址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
借助以下文本内容作為示例講解:
1
2
3
4
5
6
7
8
9
10
11
<code># tail /etc/services</code>
<code>nimgtw 48003</code><code>/udp</code> <code># Nimbus Gateway</code>
<code>3gpp-cbsp 48049</code><code>/tcp</code> <code># 3GPP Cell Broadcast Service Protocol</code>
<code>isnetserv 48128</code><code>/tcp</code> <code># Image Systems Network Services</code>
<code>isnetserv 48128</code><code>/udp</code> <code># Image Systems Network Services</code>
<code>blp5 48129</code><code>/tcp</code> <code># Bloomberg locator</code>
<code>blp5 48129</code><code>/udp</code> <code># Bloomberg locator</code>
<code>com-bardac-dw 48556</code><code>/tcp</code> <code># com-bardac-dw</code>
<code>com-bardac-dw 48556</code><code>/udp</code> <code># com-bardac-dw</code>
<code>iqobject 48619</code><code>/tcp</code> <code># iqobject</code>
<code>iqobject 48619</code><code>/udp</code> <code># iqobject</code>
7.2.1 比對列印(p)
1)列印比對blp5開頭的行
<code># tail /etc/services |sed -n '/^blp5/p'</code>
<code>blp5 48129</code><code>/tcp</code> <code># Bloomberg locator</code>
<code>blp5 48129</code><code>/udp</code> <code># Bloomberg locator</code>
2)列印第一行
<code># tail /etc/services |sed -n '1p' </code>
<code>nimgtw 48003</code><code>/udp</code> <code># Nimbus Gateway</code>
3)列印第一行至第三行
<code># tail /etc/services |sed -n '1,3p'</code>
4)列印奇數行
<code># seq 10 |sed -n '1~2p'</code>
<code>1</code>
<code>3</code>
<code>5</code>
<code>7</code>
<code>9</code>
5)列印比對行及後一行
<code># tail /etc/services |sed -n '/blp5/,+1p'</code>
6)列印最後一行
<code># tail /etc/services |sed -n '$p' </code>
7)不列印最後一行
<code># tail /etc/services |sed -n '$!p'</code>
<code>com-bardac-dw 48556</code><code>/tcp</code> <code># com-bardac-dw</code>
<code>com-bardac-dw 48556</code><code>/udp</code> <code># com-bardac-dw</code>
感歎号也就是對後面的指令取反。
8)比對範圍
<code># tail /etc/services |sed -n '/^blp5/,/^com/p'</code>
比對開頭行到最後一行:
<code># tail /etc/services |sed -n '/blp5/,$p'</code>
以逗号分開兩個樣式選擇某個範圍。
9)引用系統變量,用引号
<code># a=1</code>
<code># tail /etc/services |sed -n ''$a',3p'</code>
<code>或</code>
<code># tail /etc/services |sed -n "$a,3p"</code>
sed指令用單引号時,裡面變量用單引号引起來,或者sed指令用雙引号,因為雙引号解釋特殊符号原有意義。
7.2.2 比對删除(d)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<code># tail /etc/services |sed '/blp5/d'</code>
<code># tail /etc/services |sed '1d'</code>
<code># tail /etc/services |sed '1~2d'</code>
<code># tail /etc/services |sed '1,3d'</code>
去除空格http.conf檔案空行或開頭#号的行:
<code># sed '/^#/d;/^$/d' /etc/httpd/conf/httpd.conf</code>
删除與列印使用方法類似,可以了解是列印的取反。不用-n選項。
7.2.3 替換(s///)
1)替換blp5字元串為test
<code># tail /etc/services |sed 's/blp5/test/'</code>
<code>test</code> <code>48129</code><code>/tcp</code> <code># Bloomberg locator</code>
<code>test</code> <code>48129</code><code>/udp</code> <code># Bloomberg locator</code>
<code>matahari 49000</code><code>/tcp</code> <code># Matahari Broker</code>
<code>全局替換加g:</code>
<code># tail /etc/services |sed 's/blp5/test/g'</code>
2)替換開頭是blp5的字元串并列印
<code># tail /etc/services |sed -n 's/^blp5/test/p'</code>
3)使用&指令引用比對内容并替換
<code># tail /etc/services |sed 's/48049/&.0/'</code>
<code>3gpp-cbsp 48049.0</code><code>/tcp</code> <code># 3GPP Cell Broadcast Service Protocol</code>
<code>IP加單引号:</code>
<code># echo '10.10.10.1 10.10.10.2 10.10.10.3' |sed -r 's/[^ ]+/"&"/g'</code>
<code>"10.10.10.1"</code> <code>"10.10.10.2"</code> <code>"10.10.10.3"</code>
4)對1-4行的blp5進行替換
<code># tail /etc/services | sed '1,4s/blp5/test/' </code>
5)對比對行進行替換
<code># tail /etc/services | sed '/48129\/tcp/s/blp5/test/'</code>
6)二次比對替換
<code># tail /etc/services |sed 's/blp5/test/;s/3g/4g/'</code>
<code>4gpp-cbsp 48049</code><code>/tcp</code> <code># 3GPP Cell Broadcast Service Protocol</code>
7)分組使用,在每個字元串後面添加123
<code># tail /etc/services |sed -r 's/(.*) (.*)(#.*)/\1\2test \3/'</code>
<code>3gpp-cbsp 48049</code><code>/tcp</code> <code>test</code> <code># 3GPP Cell Broadcast Service Protocol</code>
<code>isnetserv 48128</code><code>/tcp</code> <code>test</code> <code># Image Systems Network Services</code>
<code>isnetserv 48128</code><code>/udp</code> <code>test</code> <code># Image Systems Network Services</code>
<code>blp5 48129</code><code>/tcp</code> <code>test</code> <code># Bloomberg locator</code>
<code>blp5 48129</code><code>/udp</code> <code>test</code> <code># Bloomberg locator</code>
<code>com-bardac-dw 48556</code><code>/tcp</code> <code>test</code> <code># com-bardac-dw</code>
<code>com-bardac-dw 48556</code><code>/udp</code> <code>test</code> <code># com-bardac-dw</code>
<code>iqobject 48619</code><code>/tcp</code> <code>test</code> <code># iqobject</code>
<code>iqobject 48619</code><code>/udp</code> <code>test</code> <code># iqobject</code>
<code>matahari 49000</code><code>/tcp</code> <code>test</code> <code># Matahari Broker</code>
将不變的字元串比對分組,剩餘就是要替換的,再反向引用。
8)将協定與端口号位置調換
<code># tail /etc/services |sed -r 's/(.*)(\<[0-9]+\>)\/(tcp|udp)(.*)/\1\3\/\2\4/'</code>
<code>3gpp-cbsp tcp</code><code>/48049</code> <code># 3GPP Cell Broadcast Service Protocol</code>
<code>isnetserv tcp</code><code>/48128</code> <code># Image Systems Network Services</code>
<code>isnetserv udp</code><code>/48128</code> <code># Image Systems Network Services</code>
<code>blp5 tcp</code><code>/48129</code> <code># Bloomberg locator</code>
<code>blp5 udp</code><code>/48129</code> <code># Bloomberg locator</code>
<code>com-bardac-dw tcp</code><code>/48556</code> <code># com-bardac-dw</code>
<code>com-bardac-dw udp</code><code>/48556</code> <code># com-bardac-dw</code>
<code>iqobject tcp</code><code>/48619</code> <code># iqobject</code>
<code>iqobject udp</code><code>/48619</code> <code># iqobject</code>
<code>matahari tcp</code><code>/49000</code> <code># Matahari Broker</code>
9)位置調換
<code># echo "abc:cde;123:456" |sed -r 's/([^:]+)(;.*:)([^:]+$)/\3\2\1/'</code>
<code>abc:456;123:cde</code>
10)注釋比對行後的多少行
<code># seq 10 |sed '/5/,+3s/^/#/'</code>
<code>2</code>
<code>4</code>
<code>#5</code>
<code>#6</code>
<code>#7</code>
<code>#8</code>
<code>10</code>
11)去除開頭和結尾空格或制表符
<code># echo " 1 2 3 " |sed 's/^[ \t]*//;s/[ \t]*$//'</code>
<code>1 2 3</code>
7.2.4 多重編輯(-e)
<code># tail /etc/services |sed -e '1,2d' -e 's/blp5/test/'</code>
<code>也可以使用分号分隔:</code>
<code># tail /etc/services |sed '1,2d;s/blp5/test/'</code>
7.2.5 添加新内容(a、i和c)
1)在blp5上一行添加test
<code># tail /etc/services |sed '/blp5/i \test'</code>
<code>test</code>
2)在blp5下一行添加test
<code># tail /etc/services |sed '/blp5/a \test'</code>
3)将blp5替換新行
<code># tail /etc/services |sed '/blp5/c \test'</code>
4)在指定行下一行添加一行
<code># tail /etc/services |sed '2a \test' </code>
5)在指定行前面和後面添加一行
<code># seq 5 |sed '3s/.*/txt\n&/' </code>
<code>txt</code>
<code># seq 5 |sed '3s/.*/&\ntxt/'</code>
7.2.6 讀取檔案并追加到比對行後(r)
<code># cat a.txt</code>
<code>123</code>
<code>456</code>
<code># tail /etc/services |sed '/blp5/r a.txt' </code>
7.2.7 将比對行寫到檔案(w)
<code># tail /etc/services |sed '/blp5/w b.txt'</code>
<code># cat b.txt</code>
7.2.8 讀取下一行(n和N)
n指令的作用是讀取下一行到模式空間。
N指令的作用是追加下一行内容到模式空間,并以換行符\n分隔。
1)列印比對的下一行
<code># seq 5 |sed -n '/3/{n;p}' </code>
2)列印偶數
<code># seq 6 |sed -n 'n;p' </code>
<code>6</code>
sed先讀取第一行1,執行n指令,擷取下一行2,此時模式空間是2,執行p指令,列印模式空間。 現在模式空間是2,sed再讀取3,執行n指令,擷取下一行4,此時模式空間為4,執行p指令,以此類推。
3)列印奇數
<code># seq 6 |sed 'n;d' </code>
sed先讀取第一行1,此時模式空間是1,并列印模式空間1,執行n指令,擷取下一行2,執行d指令,删除模式空間的2,sed再讀取3,此時模式空間是3,并列印模式空間,再執行n指令,擷取下一行4,執行d指令,删除模式空間的3,以此類推。
4)每三行執行一次p指令
<code># seq 6 |sed 'n;n;p' </code>
sed先讀取第一行1,并列印模式空間1,執行n指令,擷取下一行2,并列印模式空間2,再執行n指令,擷取下一行3,執行p指令,列印模式空間3。sed讀取下一行3,并列印模式空間3,以此類推。
5)每三行替換一次
方法1:
<code># seq 6 |sed 'n;n;s/^/=/;s/$/=/'</code>
<code>=3=</code>
<code>=6=</code>
我們隻是把p指令改成了替換指令。
方法2:
這次用到了位址比對,來實作上面的效果:
<code># seq 6 |sed '3~3{s/^/=/;s/$/=/}'</code>
當執行多個sed指令時,有時互相會産生影響,我們可以用大括号{}把他們括起來。
6)再看下N指令的功能
<code># seq 6 |sed 'N;q'</code>
<code>将兩行合并一行:</code>
<code># seq 6 |sed 'N;s/\n//'</code>
<code>12</code>
<code>34</code>
<code>56</code>
第一個指令:sed讀取第一行1,N指令讀取下一行2,并以\n2追加,此時模式空間是1\n2,再執行q退出。
為了進一步說明N的功能,看第二個指令:執行N指令後,此時模式空間是1\n2,再執行把\n替換為空,此時模式空間是12,并列印。
<code># seq 5 |sed -n 'N;p'</code>
<code># seq 6 |sed -n 'N;p'</code>
為什麼第一個不列印5呢?
因為N指令是讀取下一行追加到sed讀取的目前行,當N讀取下一行沒有内容時,則退出,也不會執行p指令列印目前行。
當行數為偶數時,N始終就能讀到下一行,是以也會執行p指令。
7)列印奇數行數時的最後一行
<code># seq 5 |sed -n '$!N;p' </code>
加一個滿足條件,當sed執行到最後一行時,用感歎号不去執行N指令,随後執行p指令。
7.2.9 列印和删除模式空間第一行(P和D)
P指令作用是列印模式空間的第一行。
D指令作用是删除模式空間的第一行。
1)列印奇數
<code># seq 6 |sed -n 'N;P'</code>
2)保留最後一行
<code># seq 6 |sed 'N;D' </code>
讀取第一行1,執行N指令讀取下一行并追加到模式空間,此時模式空間是1\n2,執行D指令删除模式空間第一行1,剩餘2。
讀取第二行,執行N指令,此時模式空間是3\n4,執行D指令删除模式空間第一行3,剩餘4。
以此類推,讀取最後一行列印時,而N擷取不到下一行則退出,不再執行D,是以模式空間隻剩餘6就列印。
7.2.10 保持空間操作(h與H、g與G和x)
h指令作用是複制模式空間内容到保持空間(覆寫)。
H指令作用是複制模式空間内容追加到保持空間。
g指令作用是複制保持空間内容到模式空間(覆寫)。
G指令作用是複制保持空間内容追加到模式空間。
x指令作用是模式空間與保持空間内容互換
1)将比對的内容覆寫到另一個比對
<code># seq 6 |sed -e '/3/{h;d}' -e '/5/g'</code>
h指令把比對的3複制到保持空間,d指令删除模式空間的3。後面指令再對模式空間比對5,并用g指令把保持空間3覆寫模式空間5。
2)将比對的内容放到最後
<code># seq 6 |sed -e '/3/{h;d}' -e '$G'</code>
3)交換模式空間和保持空間
<code># seq 6 |sed -e '/3/{h;d}' -e '/5/x' -e '$G'</code>
看後面指令,在模式空間比對5并将保持空間的3與5交換,5就變成了3,。最後把保持空間的5追加到模式空間的。
4)倒叙輸出
<code># seq 5 |sed '1!G;h;$!d'</code>
分析下:
1!G 第一行不執行把保持空間内容追加到模式空間,因為現在保持空間還沒有資料。
h 将模式空間放到保持空間暫存。
$!d 最後一行不執行删除模式空間的内容。
讀取第一行1時,跳過G指令,執行h将模式空間1複制到保持空間,執行d指令删除模式空間的1。
讀取第二行2時,模式空間是2,執行G指令,将保持空間1追加到模式空間,此時模式空間是2\n1,執行h将2\n1覆寫到保持空間,d删除模式空間。
讀取第三行3時,模式空間是3,執行G指令,将保持空間2\n1追加到模式空間,此時模式空間是3\n2\n1,執行h将模式空間内容複制到保持空間,d删除模式空間。
以此類推,讀到第5行時,模式空間是5,執行G指令,将保持空間的4\n3\n2\n1追加模式空間,然後複制到模式空間,5\n4\n3\n2\n1,不執行d,模式空間保留,輸出。
由此可見,每次讀取的行先放到模式空間,再複制到保持空間,d指令删除模式空間内容,防止輸出,再追加到模式空間,因為追加到模式空間,會追加到新讀取的一行的後面,循環這樣操作, 就把所有行一行行追加到新讀取行的後面,就形成了倒叙。
5)每行後面添加新空行
<code># seq 10 |sed G</code>
7.2.11 标簽
标簽可以控制流,實作分支判斷。
: lable name 定義标簽
b lable 跳轉到指定标簽,如果沒有标簽則到腳本末尾
t lable 跳轉到指定标簽,前提是s///指令執行成功
1)将換行符替換成逗号
<code># seq 6 |sed 'N;s/\n/,/'</code>
<code>1,2</code>
<code>3,4</code>
<code>5,6</code>
這種方式并不能滿足我們的需求,每次sed讀取到模式空間再列印是新行,替換\n也隻能對N指令追加後的1\n2這樣替換。
這時就可以用到标簽了:
<code># seq 6 |sed ':a;N;s/\n/,/;b a' </code>
<code>1,2,3,4,5,6</code>
看看這裡的标簽使用,:a 是定義的标簽名,b a是跳轉到a位置。
sed讀取第一行1,N指令讀取下一行2,此時模式空間是1\n2$,執行替換,此時模式空間是1,2$,執行b指令再跳轉到标簽a位置繼續執行N指令,讀取下一行3追加到模式空間,此時模式空間是1,2\n3$,再替換,以此類推,不斷追加替換,直到最後一行N讀不到下一行内容退出。
<code># seq 6 |sed ':a;N;$!b a;s/\n/,/g'</code>
先将每行讀入到模式空間,最後再執行全局替換。$!是如果是最後一行,則不執行b a跳轉,最後執行全局替換。
<code># seq 6 |sed ':a;N;b a;s/\n/,/g'</code>
可以看到,不加$!是沒有替換,因為循環到N指令沒有讀到行就退出了,後面的替換也就沒執行。
2)每三個數字加個一個逗号
<code># echo "123456789" |sed -r 's/([0-9]+)([0-9]+{3})/\1,\2/'</code>
<code>123456,789</code>
<code># echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{3})/\1,\2/;t a'</code>
<code>123,456,789</code>
<code># echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{2})/\1,\2/;t a'</code>
<code>1,23,45,67,89</code>
執行第一次時,替換最後一個,跳轉後,再對123456比對替換,直到比對替換不成功,不執行t指令。
7.2.12 忽略大小寫比對
<code># echo -e "a\nA\nb\nc" |sed 's/a/1/Ig'</code>
<code>b</code>
<code>c</code>
7.2.13 擷取總行數
<code># seq 10 |sed -n '$='</code>
<code></code>
本文轉自 李振良OK 51CTO部落格,原文連結:http://blog.51cto.com/lizhenliang/1889195,如需轉載請自行聯系原作者