Linux基礎(chǔ)命令介紹十:文本流編輯 sed
與vim不同,sed是一種非交互式的文本編輯器,同時它又是面向字符流的,每行數(shù)據(jù)經(jīng)過sed處理后輸出。
- sed [OPTION]... [script] [file]...
sed的工作過程是這樣的:首先,初始化兩個數(shù)據(jù)緩沖區(qū)模式空間和保持空間;sed讀取一行輸入(來自標(biāo)準(zhǔn)輸入或文件),去掉結(jié)尾的換行符(\n)后置于模式空間中,然后針對模式空間中的字符串開始執(zhí)行‘sed命令’,每個命令都可以有地址與之相關(guān)聯(lián),地址可以看成是條件,只有在條件成立時,相關(guān)的命令才被執(zhí)行;所有可執(zhí)行命令都處理完畢后,仍處于模式空間中的字符串會被追加一個換行符后打印輸出;之后讀取下一行輸入做同樣的處理,直到主動退出(q)或輸入結(jié)束。
地址
地址可以是如下的形式
1、number 表示行號
2、first~step 表示從first(數(shù)字)行開始,每隔step(數(shù)字)行
3、$ 表示***一行(注意當(dāng)出現(xiàn)在正則表達(dá)式中時表示行尾)
4、/regexp/ 表示匹配正則表達(dá)式regexp(關(guān)于正則表達(dá)式,請參見這一篇)
5、\%regexp% 表示匹配正則表達(dá)式regexp,%可以換成任意其他單個字符。(用于regexp包含斜線/的情況)
6、/regexp/I 匹配正則表達(dá)式regexp時不區(qū)分大小寫
7、/regexp/M 啟用正則多行模式,使$不止匹配行尾,還匹配n或r之前的位置;使^不止匹配行首,還匹配n或r之后的位置。此時可以用(\`)匹配模式空間的開頭位置,用(\')匹配模式空間的結(jié)束位置。
還可以用逗號,分隔兩個地址來表示一個范圍
表示從匹配***個地址開始,直到匹配第二個地址或文件結(jié)尾為止。如果第二個地址是個正則表達(dá)式,則不會對***個地址匹配行進(jìn)行第二個地址的匹配;如果第二個地址是行號,但小于或等于***個地址匹配行行號,則只會匹配一行(***個地址匹配行)。
8、0,/regexp/ 這種情況下,正則表達(dá)式regexp會在***行就開始進(jìn)行匹配。只有第二個地址是正則表達(dá)式時,***個地址才能用0。
9、addr1,+n表示匹配地址addr1和其后的n行。
10、addr1,~n表示從匹配地址addr1開始,直到n的倍數(shù)行為止。
如果沒有給出地址,所有的行都會匹配;在地址或地址范圍后追加字符!表示對地址取反,所有不匹配的行才會被處理。
選項(xiàng)
-n 默認(rèn)時每一行處理過的字符串都會被打印輸出,此選項(xiàng)表示關(guān)閉此默認(rèn)行為。只有被命令p作用的字符串才會被輸出。
-f file表示從file中讀取sed命令
-i 表示原地修改。應(yīng)用此選項(xiàng)時,sed會創(chuàng)建一個臨時文件,并將處理結(jié)果輸出到此文件,處理完畢后,會將此臨時文件覆蓋至原文件。
-r 表示使用擴(kuò)展的正則表達(dá)式
命令
p表示打印模式空間內(nèi)容,通常配合選項(xiàng)-n一起使用
- [root@centos7 ~]# seq 5
- 1
- 2
- 3
- 4
- 5
- [root@centos7 ~]# 只輸出第二行到第四行
- [root@centos7 ~]# seq 5|sed -n '2,4p'
- 2
- 3
- 4
- [root@centos7 ~]#
d 刪除模式空間內(nèi)容,立即處理下一行輸入。
- #刪除***一行
- [root@centos7 ~]# seq 5|sed '$d'
- 1
- 2
- 3
- 4
- [root@centos7 ~]#
q 立即退出,不再處理任何命令和輸入(只接受單個地址)
- [root@centos7 ~]# seq 5|sed '/3/q'
- 1
- 2
- 3
- [root@centos7 ~]#
n 如果沒有使用選項(xiàng)-n,輸出模式空間中內(nèi)容后,讀取下一行輸入并覆蓋當(dāng)前模式空間內(nèi)容。如果沒有更多的輸入行,sed會退出執(zhí)行。
- [root@centos7 ~]# seq 9|sed -n 'n;p'
- 2
- 4
- 6
- 8
- [root@centos7 ~]# 注意多個命令用分號分隔
s/regexp/replacement/flag 表示用replacement替換模式空間中匹配正則表達(dá)式regexp的部分。在這里符號/可以換成任意單個字符。
- [root@centos7 ~]# echo "hello123world"|sed 's/[0-9]\+/,/'
- hello,world
- #注意這里+需要轉(zhuǎn)義,如果使用選項(xiàng)-r則無需轉(zhuǎn)義
在replacement中
1、\n (n為1-9中的一個數(shù)字)表示對正則表達(dá)式中分組(...)的引用;
- [root@centos7 ~]# echo "hello123world"|sed -r 's/[a-z]+([0-9]+)[a-z]+/\1/'
- 123
- [root@centos7 ~]# echo "hello123world"|sed -r 's/([a-z]+)[0-9]+([a-z]+)/\1,\2/'
- hello,world
2、&表示模式空間中所有匹配regexp的部分;
- [root@centos7 ~]# echo "hello123world"|sed -r 's/[0-9]+/:&:/'
- hello:123:world
3、\L 將后面的字符轉(zhuǎn)化成小寫直到 \U 或 \E 出現(xiàn);
4、\l 將下一個字符轉(zhuǎn)化為小寫;
5、\U 將后面的字符轉(zhuǎn)化成大寫直到 \L 或 \E 出現(xiàn);
6、\u 將下一個字符轉(zhuǎn)化為大寫;
7、\E 停止由 \L 或 \U 起始的大小寫轉(zhuǎn)化;
- [root@centos7 ~]# echo "hello123world"|sed -r 's/^([a-z]+)[0-9]+([a-z]+)$/\U\1\E,\u\2/'
- HELLO,World
- [root@centos7 ~]#
flag
1、n數(shù)字n表示替換第n個匹配項(xiàng)
- [root@centos7 ~]# head -1 /etc/passwd
- root:x:0:0:root:/root:/bin/bash
- #替換冒號分隔的第五部分為空
- [root@centos7 ~]# head -1 /etc/passwd|sed 's/[^:]\+://5'
- root:x:0:0:/root:/bin/bash
2、g表示全局替換
- [root@centos7 ~]# echo "hello123world"|sed 's/./\U&\E/'
- Hello123world
- [root@centos7 ~]#
- [root@centos7 ~]# echo "hello123world"|sed 's/./\U&\E/g'
- HELLO123WORLD
- [root@centos7 ~]#
- #當(dāng)數(shù)字n和g同時使用時,表示從第n個匹配項(xiàng)開始替換一直到***匹配項(xiàng)
- [root@centos7 ~]# head -1 /etc/passwd|sed 's/[^:]\+://4g'
- root:x:0:/bin/bash/
3、p表示如果替換成功,則打印模式空間內(nèi)容。
4、w file表示如果替換成功,則輸出模式空間內(nèi)容至文件file中。
5、I和i表示匹配regexp時不區(qū)分大小寫。
- [root@centos7 ~]# echo 'HELLO123world'|sed -r 's/[a-z]+//Ig'
- 123
- [root@centos7 ~]#
6、M和m表示啟用正則多行模式(如前所述)。(講命令N時再舉例)
- [root@centos7 ~]# echo hello|sed 'y/el/LE/'
- hLEEo
- [root@centos7 ~]#
a text表示輸出模式空間內(nèi)容后追加輸出text內(nèi)容
- [root@centos7 ~]# seq 3|sed '1,2a hello'
- 1
- hello
- 2
- hello
- 3
- [root@centos7 ~]#
i text表示輸出模式空間內(nèi)容之前,先輸出text內(nèi)容
- [root@centos7 ~]# seq 3|sed '$ihello'
- 1
- 2
- hello
- 3
- [root@centos7 ~]#
c text表示刪除匹配地址或地址范圍的模式空間內(nèi)容,輸出text內(nèi)容。如果是單地址,則每個匹配行都輸出,如果是地址范圍,則只輸出一次。
- [root@centos7 ~]# seq 5|sed '1,3chello'
- hello
- 4
- 5
- [root@centos7 ~]# seq 5|sed '/^[^3-4]/c hello'
- hello
- hello
- 3
- 4
- hello
=表示打印當(dāng)前輸入行行號
- [root@centos7 ~]# seq 100|sed -n '$='
- 100
- [root@centos7 ~]# seq 100|sed -n '/^10\|^20/='
- 10
- 20
- 100
- [root@centos7 ~]# 轉(zhuǎn)義的|表示邏輯或
r file表示讀取file的內(nèi)容,并在當(dāng)前模式空間內(nèi)容輸出之后輸出
- [root@centos7 ~]# cat file
- hello world
- [root@centos7 ~]# seq 3|sed '1,2r file'
- 1
- hello world
- 2
- hello world
- 3
- [root@centos7 ~]#
w file表示輸出模式空間內(nèi)容至file中
N讀入一行內(nèi)容至模式空間后,再追加下一行內(nèi)容至模式空間(此時模式空間中內(nèi)容形如 line1\nline2 ),如果不存在下一行,sed會退出。
- [root@centos7 ~]# seq 10|sed -n 'N;s/\n/ /p'
- 1 2
- 3 4
- 5 6
- 7 8
- 9 10
- [root@centos7 ~]#
- #s命令的m flag舉例
- [root@centos7 ~]# seq 3|sed 'N;s/^2/xxx/'
- 1
- 2
- 3
- [root@centos7 ~]# seq 3|sed 'N;s/^2/xxx/m'
- 1
- xxx
- 3
- [root@centos7 ~]# seq 3|sed 'N;s/1$/xxx/'
- 1
- 2
- 3
- [root@centos7 ~]# seq 3|sed 'N;s/1$/xxx/M'
- xxx
- 2
- 3
D如果模式空間中沒有新行(如命令N產(chǎn)生的新行),則和命令d起同樣作用;如果包含新行,則會刪除***行內(nèi)容,然后對模式空間中剩余內(nèi)容重新開始一輪處理。(注意:D后面的命令將會被忽略)
- [root@centos7 ~]# seq 5|sed 'N;D'
- 5
- [root@centos7 ~]# seq 5|sed 'N;N;D'
- 3
- 4
- 5
P打印模式空間中***行內(nèi)容
- [root@centos7 ~]# seq 10|sed -n 'N;P'
- 1
- 3
- 5
- 7
- 9
- [root@centos7 ~]# seq 10|sed -n 'N;N;P'
- 1
- 4
- 7
- #注意另一種寫法輸出中的不同
- [root@centos7 ~]# seq 10|sed -n '1~3P'
- 1
- 4
- 7
- 10
g用保持空間中的內(nèi)容替換模式空間中的內(nèi)容
- [root@centos7 ~]# seq 5|sed -n 'g;N;s/\n/xx/p'
- xx2
- xx4
- [root@centos7 ~]#
G追加一個換行符到模式空間,然后再將保持空間中的內(nèi)容追加至換行符之后。(此時模式空間中內(nèi)容形如 PATTERN\nHOLD )
- [root@centos7 ~]# seq 5|sed 'G;s/\n/xx/'
- 1xx
- 2xx
- 3xx
- 4xx
- 5xx
h用模式空間中的內(nèi)容替換保持空間中的內(nèi)容(注意此時模式空間中的內(nèi)容并沒有被清除)
- [root@centos7 ~]# seq 5|sed -n 'h;G;s/\n/xx/p'
- 1xx1
- 2xx2
- 3xx3
- 4xx4
- 5xx5
- [root@centos7 ~]# seq 5|sed -n 'h;G;G;s/\n/xx/gp'
- 1xx1xx1
- 2xx2xx2
- 3xx3xx3
- 4xx4xx4
- 5xx5xx5
H追加一個換行符到保持空間,然后再將模式空間中的內(nèi)容追加至換行符之后。(此時保持空間中內(nèi)容形如 HOLD\nPATTERN )
- [root@centos7 ~]# seq 3|sed -n 'H;G;s/\n/xx/gp'
- 1xxxx1
- 2xxxx1xx2
- 3xxxx1xx2xx3
- [root@centos7 ~]#
x交換模式空間和保持空間的內(nèi)容
- [root@centos7 ~]# seq 9|sed -n '1!{x;N};s/\n//p'
- 3
- 25
- 47
- 69
- #處于{...}之中的是命令組
: label為分支命令指定標(biāo)簽位置(不允許地址匹配)
b label無條件跳轉(zhuǎn)到label分支,如果省略了label,則跳轉(zhuǎn)到整條命令結(jié)尾(即開始下一次讀入)
- #如刪除xml文件中注釋部分(<!--...-->之間的部分是注釋,可以多行)
- sed '/<!--/{:a;/-->/!{N;ba};d}' server.xml
- #表示匹配<!--開始,在匹配到-->之前一直執(zhí)行N,匹配到-->之后刪除模式空間中內(nèi)容
- #如在nagios的配置文件中,有許多define host{...}的字段,如下所示:
- define host{
- use windows-server
- host_name serverA
- hostgroups 060202
- alias 060202
- contact_groups yu
- address 192.168.1.1
- }
- #現(xiàn)在需要刪除ip地址是192.168.1.1的段,可以這樣:
- sed -i '/define host/{:a;N;/}/!ba;/192\.168\.1\.1/d}' file
- #注意和前一個例子中的區(qū)別
t label在一次輸入后有成功執(zhí)行的s替換命令才跳轉(zhuǎn)到label,如果省略了label,則跳轉(zhuǎn)到整條命令結(jié)尾(即開始下一次讀入)
- #如行列轉(zhuǎn)換
- [root@centos7 ~]# seq 10|sed ':a;$!N;s/\n/,/;ta'
- 1,2,3,4,5,6,7,8,9,10
- [root@centos7 ~]#
- #如將MAC地址78A35114F798改成帶冒號的格式78:A3:51:14:F7:98
- [root@centos7 temp]# echo '78A35114F798'|sed -r ':a;s/\B\w{2}\b/:&/;ta'
- 78:A3:51:14:F7:98
- [root@centos7 temp]#
- #這里\b表示匹配單詞邊界,\B表示匹配非單詞邊界的其他任意字符
- #當(dāng)然也可以采用其他的方式實(shí)現(xiàn):
- [root@centos7 temp]# echo '78A35114F798'|sed -r 's/..\B/&:/g'
- 78:A3:51:14:F7:98
- [root@centos7 temp]#
T label在一次輸入后只要沒有替換命令被成功執(zhí)行就跳轉(zhuǎn)到label,如果省略了label,則跳轉(zhuǎn)到整條命令結(jié)尾(即開始下一次讀入)
z表示清除模式空間中內(nèi)容,和s/.*//起相同的作用,但更有效。
更多例子
1、刪除匹配行的上一行和下一行
- #例如輸入數(shù)據(jù)為命令seq 10的輸出(當(dāng)然也可以是任意其他文件內(nèi)容)
- #要求刪除匹配5那一行的前一行和后一行
- [root@centos7 temp]# seq 10|sed -n '$!N;/\n5/{s/.*\n//p;N;d};P;D'
- 1
- 2
- 3
- 5
- 7
- 8
- 9
- 10
2、合并奇偶數(shù)行
- #輸入數(shù)據(jù)為命令seq 11的輸出,要求分別將奇數(shù)和偶數(shù)分別放在同一行
- #輸出***行`1 3 5 7 9 11`,第二行`2 4 6 8 10`
- [root@centos7 ~]# seq 11|sed -nr '$!N;2!G;s/([^\n]+)\n((.+)\n)?(.+)\n(.+)/\4 \1\n\5 \3/;h;$p'
- 1 3 5 7 9 11
- 2 4 6 8 10
- [root@centos7 ~]#
3、合并多文件
- #文本a.txt的內(nèi)容:
- 01 12510101 4001
- 02 12310001 4002
- 03 12550101 4003
- 04 12610001 4004
- 05 12810001 4005
- 06 12310001 4006
- 07 12710001 4007
- 08 12310001 4008
- 09 12810101 4009
- 10 12510101 4010
- 11 12310001 4011
- 12 12610001 4012
- 13 12310001 4013
- #文本b.txt的內(nèi)容:
- A 12410101 2006/02/15 2009/01/31 4002
- B 12310001 2006/08/31 2008/08/29 4001
- C 12610001 2008/05/23 2008/05/22 4002
- D 12810001 1992/12/10 1993/06/30 4001
- E 12660001 1992/05/11 1993/06/01 4005
- #要求輸出a.txt內(nèi)容中第二列和b.txt中第二列相同的行,并追加b.txt中對應(yīng)的兩個日期列。
- #形如:02 12310001 4002 2006/08/31 2008/08/29
- sed -rn '/^[01]/ba;H;:a;G;s/^((..)( .*)( [^\n]+)).*\3(( [^ ]*){2}).*/\1\5/p' b.txt a.txt
- #當(dāng)然如果使用awk來處理的話,解決思路更容易理解一些:
- awk 'NR==FNR{a[$2]=$3FS$4;next}{if($2 in a)print $0,a[$2]}' b.txt a.txt
為加深對sed各種命令特性的理解,請自行分析這三個例子。
各種命令的組合使用,再加上正則表達(dá)式的強(qiáng)大能力,使得sed可以處理所有能夠計(jì)算的問題。但由于代碼可讀性不強(qiáng),理解起來比較困難,通常使用sed作為一個文本編輯器,對文本做非交互的流式處理。理解上述各個命令的含義,熟練使用它們,就會發(fā)現(xiàn)sed的強(qiáng)大之處。