全網最詳細的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 作業練習:
- 把/etc/passwd 複製到/root/test.txt,用sed列印所有行
- 列印test.txt的3到10行
- 列印test.txt 中包含 ‘root’ 的行
- 刪除test.txt 的15行以及以後所有行
- 刪除test.txt中包含 ‘bash’ 的行
- 替換test.txt 中 ‘root’ 為 ‘toor’
- 替換test.txt中 ‘/sbin/nologin’ 為 ‘/bin/login’
- 刪除test.txt中5到10行中所有的數字
- 刪除test.txt 中所有特殊字元(除了數字以及大小寫字母)
- 把test.txt中第一個單詞和最後一個單詞調換位置
- 把test.txt中出現的第一個數字和最後一個單詞替換位置
- 把test.txt 中第一個數字移動到行末尾
- 在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