1. 程式人生 > >全網最詳細的Linux命令系列-sed文字處理命令

全網最詳細的Linux命令系列-sed文字處理命令

Sed簡介

SED是一個非互動式文字編輯器,它可對文字檔案和標準輸入進行編輯,標準輸入可以來自鍵盤輸入、文字重定向、字串、變數,甚至來自於管道的文字,與VIM編輯器類似,它一次處理一行內容,Sed可以編輯一個或多個檔案,簡化對檔案的反覆操作、編寫轉換程式等。

Sed命令的原理:在處理文字時把當前處理的行儲存在臨時緩衝區中,稱為“模式空間”(pattern space),緊接著用SED命令處理緩衝區中的內容,處理完成後把緩衝區的內容輸出至螢幕或者寫入檔案。逐行處理直到檔案末尾,然而如果列印在螢幕上,實質檔案內容並沒有改變,除非你使用重定向儲存輸出或者寫入檔案。

Sed語法引數

引數格式為:

sed    [-Options]     [‘Commands’]    filename;

sed工具預設處理文字,文字內容輸出螢幕已經修改,但是檔案內容其實沒有修改,需要加-i引數即對檔案徹底修改;

-e<script>或--expression=<script>   以選項中指定的script來處理輸入的文字檔案。
-f<script檔案>或--file=<script檔案>   以選項中指定的script檔案來處理輸入的文字檔案。
-h                                      或--help 顯示幫助。
-n或--quiet或--silent                僅顯示script處理後的結果。
-r  使用擴充套件正則表示式
x                               #x為指定行號;
x,y                             #指定從x到y的行號範圍;
/pattern/                       #查詢包含模式的行;
/pattern/,/pattern/             #查詢包含兩個模式的行;
/pattern/,x                     #從與pattern的匹配行到x號行之間的行;
x,/pattern/                     #從x號行到與pattern的匹配行之間的行;
x,y!                            #查詢不包括x和y行號的行;
r                                 #從另一個檔案中讀檔案;
w                                 #將文字寫入到一個檔案;
y                                 #變換字元;
q                                     #第一個模式匹配完成後退出;
l                                 #顯示與八進位制ASCII碼等價的控制字元;
{}                                #在定位行執行的命令組;
p                                 #列印匹配行;
=                                 #列印檔案行號;
a\                                #在定位行號之後追加文字資訊;
i\                                #在定位行號之前插入文字資訊;
d                                 #刪除定位行;
c\                                #用新文字替換定位文字;
s                                 #使用替換模式替換相應模式;
&                           #引用已匹配字串
first~step                  #步長,每 step 行,從第 first 開始
$                           #匹配最後一行
/regexp/                    #正則表示式匹配行
number                      #只匹配指定行
addr1,addr2                 #開始匹配 addr1 行開始,直接 addr2 行結束
addr1,+N                    #從 addr1 行開始,向後的 N 行
addr1,~N                    #從 addr1 行開始,到 N 行結束

Sed例項練習(模式空間)

示例文字:

[root@localhost ~]# cat test.txt 
www.test.net
www.baidu.com
www.taobao.com
www.sina.com
old old old
new new new

替換test.txt文字中old為new:

sed    's/old/new/g'       test.txt

列印test.txt文字第一行至第三行:

sed    -n  '1,3p'           test.txt

列印test.txt文字中第一行與最後一行:

sed    -n '1p;$p'           test.txt

刪除test.txt第一行至第三行、刪除匹配行至最後一行:

sed       '1,3d'             test.txt
sed       '/test/,$d'         test.txt

刪除test.txt最後6行及刪除最後一行:

for   i  in `seq 1 6`;do  sed  -i   '$d'  test.txt ;done
sed       '$d'               test.txt

刪除test.txt最後一行:

sed       '$d'             test.txt

在test.txt查詢test所在行,並在其下一行新增word字元,a表示在其下一行新增字串:

sed    '/test/aword'      test.txt

在test.txt查詢test所在行,並在其上一行新增word字元,i表示在其上一行新增字串:

sed    '/test/i word'       test.txt

在test.txt查詢以test結尾的行尾新增字串word,$表示結尾標識,&在Sed中表示新增:

sed   's/test$/& word/g'     test.txt

在test.txt查詢www的行,在其行首新增字串word,^表示起始標識,&在Sed中表示新增:

sed   '/www/s/^/& word/'    test.txt

多個sed命令組合,使用-e引數:

sed  -e  '/www.jd.com/s/^/&1./'  -e  's/www.jd.com$/&./g'  test.txt

多個sed命令組合,使用分號“;”分割:

sed  -e  '/www.jd.com/s/^/&1./;s/www.jd.com$/&./g'  test.txt

Sed讀取系統變數,變數替換:

TEST=WWW.test.NET
Sed  “s/www.jd.com/$TEST/g” test.txt

修改Selinux策略enforcing為disabled,查詢/SELINUX/行,然後將其行enforcing值改成disabled、!s表示不包括SELINUX行:

sed  -i   '/SELINUX/s/enforcing/disabled/g' /etc/selinux/config
sed  -i   '/SELINUX/!s/enforcing/disabled/g' /etc/selinux/config

去除空格httpd.conf檔案空行或者是#號開頭的行

sed '/^#/d;/^$/d' /etc/httpd/conf/httpd.conf

列印是把匹配的打印出來,刪除是把匹配的刪除,刪除只是不用-n 選項
IP 加單引號

echo '10.10.10.1 10.10.10.2 10.10.10.3' |sed -r 's/[^ ]+/"&"/g'
"10.10.10.1" "10.10.10.2" "10.10.10.3"

分組() 匹配

對 1-4 行的 html 進行替換
示例檔案:

http://www.baidu.com/index.html
http://www.baidu.com/1.html
http://post.baidu.com/index.html
http://mp3.baidu.com/index.html
http://www.baidu.com/3.html
http://post.baidu.com/2.html
tail  /etc/services | sed '1,4 s/html/txt/'
http://www.baidu.com/index.txt
http://www.baidu.com/1.txt
http://post.baidu.com/index.txt
http://mp3.baidu.com/index.txt
http://www.baidu.com/3.html
http://post.baidu.com/2.html

分組使用,在第2列後面新增 test

tail /etc/services |sed -r 's/(.*) (.*)(#.*)/\1\2test \3/'
3gpp-cbsp 48049/tcp test # 3GPP Cell Broadcast Service
isnetserv 48128/tcp test # Image Systems Network Services
isnetserv 48128/udp test # Image Systems Network Services
blp5 48129/tcp test # Bloomberg locator
blp5 48129/udp test # Bloomberg locator
com-bardac-dw 48556/tcp test # com-bardac-dw
com-bardac-dw 48556/udp test # com-bardac-dw
iqobject 48619/tcp test # iqobject
iqobject 48619/udp test # iqobject 

第一列是第一個小括號匹配,第二列第二個小括號匹配,第三列一樣。將不變的字串匹配分組,再通過\數字按分組順序反向引用。

基本正則表示式中支援分組,而在擴充套件正則表示式中,分組的功能更加強大,也可以說才是真正的分組

():分組,後面可以使用\1 \2 \3...引用前面的括號分組。

處理以下檔案內容,將域名取出並進行計數排序,如處理:

http://www.baidu.com/index.html
http://www.baidu.com/1.html
http://post.baidu.com/index.html
http://mp3.baidu.com/index.html
http://www.baidu.com/3.html
http://post.baidu.com/2.html

得到如下結果:
域名的出現的次數域名

[root@localhost shell]# cat file | sed -e ' s/http:\/\///' -e ' s/\/.*//' | sort | uniq -c | sort -rn
3 www.baidu.com
2 post.baidu.com
1 mp3.baidu.com
[root@codfei4 shell]# awk -F/ '{print $3}' file |sort -r|uniq -c|awk '{print $1"\t",$2}'
3 www.baidu.com
2 post.baidu.com
1 mp3.baidu.com*

將協議與埠號位置調換

tail /etc/services |sed -r 's/(.*)(\<[0-9]+\>)\/(tcp|udp)(.*)/\1\3\/\2\4/'
3gpp-cbsp tcp/48049 # 3GPP Cell Broadcast Service
isnetserv tcp/48128 # Image Systems Network Services
isnetserv udp/48128 # Image Systems Network Services
blp5 tcp/48129 # Bloomberg locator
blp5 udp/48129 # Bloomberg locator
com-bardac-dw tcp/48556 # com-bardac-dw
com-bardac-dw udp/48556 # com-bardac-dw
iqobject tcp/48619 # iqobject
iqobject udp/48619 # iqobject
matahari tcp/49000 # Matahari Broker

字元位置調換
替換 x 字元為大寫:

# echo "abc cde xyz" |sed -r 's/(.*)x/\1X/'
abc cde Xyz

456 與 cde 調換:

# echo "abc:cde;123:456" |sed -r 's/([^:]+)(;.*:)([^:]+$)/\3\2\1/'
abc:456;123:cde

註釋匹配行後的多少行

[root@localhost ~]# sed '/5/,+3s/^/@/' 1.txt 
1 
2 
3 
4 
@5 
@6 
@7 
@8 
9 
10

註釋指定多行

[root@localhost ~]# sed -r 's/^3|^5|^7/#&/' 1.txt 
1 
2 
#3 
4 
#5 
6 
#7 
8 
9 
10

去除開頭和結尾空格或製表符

echo " 1 2 3 " |sed 's/^[ \t]*//;s/[ \t]*$//'
1 2 3

Sed例項練習(保留空間)

Sed之所以能以行為單位進行修改文字或者編輯文字,主要原因是因為它使用了兩個空間:一個是活動的“模式空間”,另一個是輔助作用的“保留空間”.
模式空間:可以想象成工程裡面的流水線,所有的資料讀取過來之後就直接在上面進行工作。
保留空間:可以想象成是倉庫,我們在進行資料處理的時候,會把資料讀取到保留空間中,作為資料的暫存區域,需要時再進行調出。

SED高階命令可以分為三種功能:

N、D、P:處理多行模式空間的問題;
H、h、G、g、x:將模式空間的內容放入保留空間以便接下來的編輯;
:、b、t:在指令碼中實現分支與條件結構(標籤)。
n                              #讀取下一個輸入行,用下一個命令處理新的行;
N                        #將當前讀入行的下一行讀取到當前的模式空間。
d                                      #刪除模式空間的所有內容,開始下一個迴圈
D                        #刪除模式空間的第一行,開始下一個迴圈;
p                                      #列印當前模式空間的所有內容;
P                                      #列印模式空間的第一行,開始下一個迴圈
h                              #將模式緩衝區的文字複製到保持緩衝區;
H                              #將模式緩衝區的文字追加到保持緩衝區;
x                              #互換模式緩衝區和保持緩衝區的內容;
g                              #將保持緩衝區的內容複製到模式緩衝區;
G                              #將保持緩衝區的內容追加到模式緩衝區。

在test.txt每行後加入空行,也即每行佔永兩行空間,每一行後邊插入一行空行、兩行空行及前三行每行後插入空行:

sed     '/^$/d;G'            test.txt  #刪除的是匹配的條件行,留下的是模式空間處理的行,G引數是將保持空間內的行追加到模式空間去(保持空間預設是空的)。
sed     '/^$/d;G;G'            test.txt  #後面跟2個G 是將G保持空間內的兩行追加到模式空間
sed     '/^$/d;1,3G;'       test.txt     #1,3G 是隻將追加3行到模式空間

將test.txt偶數行刪除及隔兩行刪除一行:

sed    'n;d'              test.txt      #n引數將輸入行的下一行顯示出來,而後面的d 正好會將顯示的行進行刪除,測試“sed –n ‘n;p’ file ”
sed    'n;n;d'           test.txt,   #2個n引數將輸入的的下兩行顯示出來,正好使用d都刪除,形成隔兩行刪一行的效果。

在test.txt匹配行前一行、後一行插入空行以及同時在匹配前後插入空行:

sed  '/test/{x;p;x;}'      test.txt     #x引數是讓模式空間和保持空間互相交換,匹配test時,將模式空間內的資料換成了保持空間內的資料,保持空間預設是空的,所以前一行會變成空行,然後將空行打印出來
sed  '/test/G'                test.txt  #G引數配置上之後將保持空間的內容追加到模式空間去。
sed  '/test/{x;p;x;G;}'   test.txt      #綜合以上兩個引數配置。

在test.txt每行前加入順序數字序號、加上製表符\t及.符號:

sed = test.txt| sed 'N;s/\n/ /'   # “=”等號列印當前行號碼。N引數是配置將當前讀入行的下一行讀取到當前的模式空間,就是把下一行讀到當前的模式空間來,利用s替換換行符,形成數字順序效果。
sed = test.txt| sed 'N;s/\n/\t/'  #案例同上
sed = test.txt| sed 'N;s/\n/\./'  #案例同上

刪除test.txt行前和行尾的任意空格:

sed 's/^[ \t]*//;s/[ \t]*$//' test.txt  #s替換 刪除行前

列印test.txt關鍵詞old與new之間的內容:

sed -n '/old/,/new/'p     test.txt   #-n引數可以列印匹配的條件內容行

列印及刪除test.txt最後兩行:

sed   '$!N;$!D'             test.txt 
N  讀取下一行並追加輸入到模式空間
D  刪除模式空間的第一行,開始下一個迴圈
#讀取1,$!條件滿足(不是尾行,#N前加$!表示末尾行不執行N),執行N命令,讀取下一行並追加輸入到模式空間,得出1\n2。執行$!D,不是最後一行,所以執行D,刪除模式空間的第一行,開始下一個迴圈,模式空間由1\n2成了2。讀取第二行,執行 N 命令,此時模式空間是 3\n4,執行 D 命令刪除模式空間第一行 3,剩餘4,直到執行N讀入第5行,$!條件不滿足(不是尾行),不執行N命令:繼續讀入6行,這裡模式空間為:5\n6,$!D,因為是最後一行,所以不執行D,控制流到達指令碼底部,輸出模式空間的內容
sed   'N;$!P;$!D;$d'      test.txt
P  列印模式空間的第一行
N  讀取下一行並追加輸入到模式空間
D  刪除模式空間的第一行,開始下一個迴圈
d 刪除匹配的行
#讀取第一行,執行N,此時得出1\n2,P列印從開始到第一個\n的內容,執行$!D,不是最後一行,所以執行D,刪除模式空間的第一行,開始下一個迴圈,模式空間由1\n2成了2,$d 是因為不是末行所以不執行,讀取第二行,執行 N 命令,此時模式空間是 3\n4,執行 P 命令顯示模式空間第一行 3,執行D,刪除模式空間的第一行,剩餘4,讀取第5行,執行N,將第6行進行讀取,執行$!P,因為是最後一行不執行P,執行$!D,因為是最後一行,不執行D,最後執行$d,刪除模式空間5/6行。

合併上下兩行,也即兩行合併:

sed    '$!N;s/\n/ /'          test.txt  #N前加$!表示末尾行不執行N
sed    'N;s/\n/ /'            test.txt

Sed中標籤使用(: 、b 和 和 t )

標籤可以控制流,實現分支判斷。

:           lable name 定義標籤
b       lable 跳轉到指定標籤,如果沒有標籤則到指令碼末尾
t          lable 跳轉到指定標籤,前提是 s///命令執行成功

將換行符替換成逗號
方法 1:

seq 6 |sed 'N;s/\n/,/'
1,2
3,4
5,6

這種方式並不能滿足我們的需求,每次 sed 讀取到模式空間再列印是新行,替換\n 也只能對 N 命令,追加後的 1\n2 這樣替換。
這時就可以用到標籤了:

seq 6 |sed ':a;N;s/\n/,/;b a'
1,2,3,4,5,6
看看這裡的標籤使用,:a 是定義的標籤名,b a 是跳轉到 a 位置。
sed 讀取第一行 1,N 命令讀取下一行 2,此時模式空間是 1\n2$,執行替換,此時模式空間是1,2$,執行 b 命令再跳轉到標籤 a 位置繼續執行 N 命令,讀取下一行 3 追加到模式空間,此時模式空間是 1,2\n3$,再替換,以此類推,不斷追加替換,直到最後一行 N 讀不到下一行內容退出。

方法 2:

seq 6 |sed ':a;N;$!b a;s/\n/,/g'
1,2,3,4,5,6
先將每行讀入到模式空間,最後再執行全域性替換。$!是如果是最後一行,則不執行 b a 跳轉,最後執行全域性替換。
seq 6 |sed ':a;N;b a;s/\n/,/g'
1
2
3
4
5
6
可以看到,不加$!是沒有替換,因為迴圈到 N 命令沒有讀到行就退出了,後面的替換也就沒執行。

每三個數字加個一個逗號

# echo "123456789" |sed -r 's/([0-9]+)([0-9]+{3})/\1,\2/'

123456,789

# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{3})/\1,\2/;t a'

123,456,789

# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{2})/\1,\2/;t a'

1,23,45,67,89
執行第一次時,替換最後一個,跳轉後,再對 123456 匹配替換,直到匹配替換不成功,不執行 t 命令。

忽略大小寫匹配(I )

# echo -e "a\nA\nb\nc" |sed 's/a/1/Ig'

1
1
b
c

獲取總行數(# )

seq 10 |sed -n '$='

Sed 指令碼使用編寫方法
<1>從檔案讀入命令

sed -f sed.sh
sed.sh檔案內容:
s/root/yerik/p
s/bash/csh/p

<2>直接執行指令碼 ./sed.sh /etc/passwd

#!/bib/sed -f
s/root/yerik/p
s/bash/csh/p

Sed 擴充套件練習高階替換

1,刪除檔案每行的第一個字元。

sed -n 's/^.//gp' /etc/passwd
sed -nr 's/(.)(.*)/\2/p' /etc/passwd
sed -r 's/^.//g' test.txt

2,刪除檔案每行的第二個字元。

sed -nr 's/(.)(.)(.*)/\1\3/p' /etc/passwd
sed -r 's/(^.)(.)/\1/g' test.txt

3,刪除檔案每行的最後一個字元。

sed -nr 's/.$//p' /etc/passwd
sed -nr 's/(.*)(.)/\1/p' /etc/passwd
sed -r 's/(.)$//g' test.txt

4,刪除檔案每行的倒數第二個字元。

sed -nr 's/(.*)(.)(.)/\1\3/p' /etc/passwd
sed -r 's/(.)(.)$/\2/g' test.txt

5,刪除檔案每行的第二個單詞。

sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\1\2\3\5/p' /etc/passwd
sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)(.*)/\1\2\4\5/' test.txt

6,刪除檔案每行的倒數第二個單詞。

sed -nr 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]*)/\1\2\4\5\6/p' /etc/passwd
sed -r 's/([a-Z]+)([^a-Z])([a-Z]+)$/\2\3/g' test.txt

7,刪除檔案每行的最後一個單詞。

sed -nr 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]*)/\1\2\4/p' /etc/passwd
sed -r 's/([a-Z]+)$//g' test.txt
sed -r 's/(.*)([^a-Z]+)([a-Z]+)/\1\2/' test.txt

8,交換每行的第一個字元和第二個字元。

sed -nr 's/(.)(.)(.*)/\2\1\3/p' /etc/passwd
sed -r 's/(.)(.)/\2\1/' test.txt

9,交換每行的第一個字元和第二個單詞

sed -r 's/(^.)([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)/\5\2\3\4\1/' test.txt

9,交換每行的第一個單詞和第二個單詞。

sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\1\4\3\2\5/p' /etc/passwd

10,交換每行的第一個單詞和最後一個單詞。

sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)/\5\2\3\4\1/' test.txt

11,刪除一個檔案中所有的數字。

sed 's/[0-9]*//g' test.txt

12,刪除每行開頭的所有空格。

sed -n 's/^\ *//p' /etc/passwd 
sed -nr 's/( *)(.*)/\2/p' test.txt
sed 's/^ *//' test.txt

13,用製表符替換檔案中出現的所有空格。

sed -n 's/\ /\t/gp' test.txt
sed -r 's/( +)/\t/g' test.txt
sed 's/ /\t/g' test.txt

14,把所有大寫字母用括號()括起來。

sed -nr 's/([A-Z])/(&)/gp' test.txt
sed -n 's/[A-Z]/(&)/gp' test.txt

15,列印每行3次。

sed 'p;p' test.txt
sed -n 'p;p;p'  test.txt

16,隔行刪除。

sed -n '1~2p' test.txt
sed '1d;n;d' ww.txt
sed '1~2d' ww.txt 
sed '0~2d' ww.txt

17,把檔案從第22行到第33行復制到第44行後面。

sed '1,21h;22h;23,33H;44G' pass
cat -n /etc/passwd | sed '22h;23,33H;44G'

18,把檔案從第22行到第33行移動到第44行後面。

sed '22{h;d};23,33{H;d};44G' pass
cat -n /etc/passwd | sed '22{h;d};23,33{H;d};44G'

19,只顯示每行的第一個單詞。

sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)(.*)/\2/p' /etc/passwd
sed -r 's/([a-Z]+)(.*)/\1/'  test.txt

20,列印每行的第一個單詞和第三個單詞。

sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\2--\4/p' /etc/passwd
sed -r 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\2\6/'  test.txt

21,將格式為 mm/yy/dd 的日期格式換成 mm;yy;dd

date +%m/%Y/%d |sed -n 's#/#;#gp'

22, 逆向輸出

cat a.txt
ABC
DEF
XYZ
sed '1!G;h;$!d' a.txt
輸出樣式變成
XYZ
DEF
ABC

Sed 作業練習:

  1. 把/etc/passwd 複製到/root/test.txt,用sed列印所有行
  2. 列印test.txt的3到10行
  3. 列印test.txt 中包含 ‘root’ 的行
  4. 刪除test.txt 的15行以及以後所有行
  5. 刪除test.txt中包含 ‘bash’ 的行
  6. 替換test.txt 中 ‘root’ 為 ‘toor’
  7. 替換test.txt中 ‘/sbin/nologin’ 為 ‘/bin/login’
  8. 刪除test.txt中5到10行中所有的數字
  9. 刪除test.txt 中所有特殊字元(除了數字以及大小寫字母)
  10. 把test.txt中第一個單詞和最後一個單詞調換位置
  11. 把test.txt中出現的第一個數字和最後一個單詞替換位置
  12. 把test.txt 中第一個數字移動到行末尾
  13. 在test.txt 20行到末行最前面加 ‘aaa:’

sed習題答案

1. /bin/cp /etc/passwd  /root/test.txt ;  sed -n '1,$'p test.txt
2. sed -n '3,10'p test.txt
3. sed -n '/root/'p test.txt
4. sed '15,$'d  test.txt
5. sed '/bash/'d test.txt
6. sed 's/root/toor/g' test.txt
7. sed 's#sbin/nologin#bin/login#g' test.txt
8. sed '5,10s/[0-9]//g' test.txt
9. sed 's/[^0-9a-zA-Z]//g' test.txt
10. sed 's/\(^[a-Z]*\)\([^a-Z].*\)\([^a-Z]\)\([a-Z]*$\)/\4\2\3\1/' test.txt
11. sed 's#\([^0-9][^0-9]*\)\([0-9][0-9]*\)\([^0-9].*\)\([^a-zA-Z]\)\([a-zA-Z][a-zA-Z]*$\)#\1\5\3\4\2#' test.txt
12. sed 's#\([^0-9][^0-9]*\)\([0-9][0-9]*\)\([^0-9].*$\)#\1\3\2#' test.txt
13. sed '20,$s/^.*$/aaa:&/' test.txt