天天看點

第七章 Shell文本處理三劍客之sed

本章大綱:

<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

寫入目前模式空間到檔案

!

取反、否定

&amp;

引用已比對字元串

位址

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)使用&amp;指令引用比對内容并替換

<code># tail /etc/services |sed 's/48049/&amp;.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/[^ ]+/"&amp;"/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/(.*)(\&lt;[0-9]+\&gt;)\/(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&amp;/' </code>

<code>txt</code>

<code># seq 5 |sed '3s/.*/&amp;\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,如需轉載請自行聯系原作者