sed是我在linux上最喜歡的命令之一!(其實我很想說sed說是我最喜歡的命令,不過linux我喜歡的逆天的命令實在是太多了,所以sed就是其中“之一”了吧。)
之前一直覺得sed的格式亂亂的,又是sed '1,2p' fileName
,又是sed 's/old/new/g' fileName
的,格式千差萬別,毫無規律可循!直到今天看到了這篇文章,終於看到了統一點!
sed的用法就是:
sed [options] [commands] [input-file]
options就是帶槓的參數,input-file就是文件的名字,關鍵在於commands!
不過commands也是有固定格式的:
[範圍][指令]
關鍵在於[range]和[command]格式都比較豐富,所以組合起來才顯得比較亂。
範圍
先來說說範圍!
最簡單的範圍莫過於——
‘m,n’表示從m行到n行: sed '2,3p' fileName
,指處理2~3行;
‘m,+n’表示從m行到m+n行: sed '2,+3p' fileName
,指處理2~(2+3=5)行。
‘m~n’表示從m行開始每n行匹配一次: 如1~2表示只匹配奇數行,2~2表示只匹配偶數行。
特殊符號包括:$
表示最後一行;0
表示第一行前,也就是最開始;匹配後加!
表示不匹配以上條件的行(類似取反)。如sed '1~2d' file #刪除奇數行
而sed '1~2!d' file #刪除偶數行
。
複雜一點的範圍——
使用正則表達式匹配範圍:'3,/hello/'
指的是從第3行到第一次出現hello的行的這一部分。比如sed '3,/hello/d' fileName
指刪除3~”第一次出現hello的行”。sed -n '/test/,/check/p' fileName
指打印test到check的行。
正式因爲這些匹配的方式多樣,導致他們和命令組合起來之後顯得比較多變。
指令
比較簡單的指令也比較好看懂——
比如d
,和前面的範圍組合起來之後,簡單的就是sed '2,5d' fileName
,刪除2~5行;稍複雜點兒就是sed '2,/hello/d'
,刪除2~“第一次出現hello的行”。
複雜的指令和之前的範圍組合起來就不容易看懂了——
比如s
,s
是個自帶一大串參數的指令,也是用的最多的指令。其格式爲s/oldString/newString/flags
,那麼和前面的範圍組合起來就把commands這一塊兒變成了:[range]s/oldString/newString/[flags]
。
這樣的話整個sed命令就變成了:
sed [options] '[range]s/oldString/newString/[flags]' [filename]
比如sed '2,$s/hi/hello/g' fileName
表示從第二行到最後一行,將所有的hi替換成hello。
如果[範圍]
再複雜一點兒,變成sed '2,/haha/s/hi/hello/g' fileName
,表示從第二行到第一次出現haha的行,將所有的hi替換爲hello;sed '/haha/s/hi/hello/g'
表示將所有含有haha的行的所有hi替換爲hello。
這樣只要知道sed命令中間的commands部分由範圍和指令組成,在分別搞懂了這兩部分的形式,他們再組合起來就不顯得混亂了。
一些知識點
-ibak
sed的-i
命令是覆蓋源文件,以後建議使用-ibak
,比如sed '2,+8d' hello.txt
,在刪除hello.txt文件的2~10行之前,會先生成一個名爲hello.txtbak的備份文件,有備份總是好事兒,大不了再手動刪一次,總比sed命令改壞了源文件欲哭無淚強!
-n
和p
p
一般都是和-n
同時出現,可以起到只顯示被處理的行的效果。
比如,對於DreamList.puppy文件,原內容爲:
lgl@pArch ~/tmp/sed $ cat DreamList.puppy
Drm Lst wth Pppy
- S th srs
- Slf-stdy MB
- Tk phts rd MB
- St sd th lk f r rd d tlk dr sst
- t brkfst vry dy, lch vry dy, dr vry dy
- R th plyrd d Strtch th bds ftr tht
- Wtch mvs
- Wt tsd th rl's drmtry
- t hr wrkplc t wt hr t ff dty
- Rd tsd th cmps
- Wtch xhbts sh hs trsts
- Sy LV Y vry dy vry mmt
- t FST, FST, FST f r w
- Mk ... r... Prdc LV
進行替換處理,將所有出現S的行的小橫槓換成五個#:
lgl@pArch ~/tmp/sed $ sed '/S/s/-/#####/g' DreamList.puppy
Drm Lst wth Pppy
##### S th srs
##### Slf#####stdy MB
- Tk phts rd MB
##### St sd th lk f r rd d tlk dr sst
- t brkfst vry dy, lch vry dy, dr vry dy
##### R th plyrd d Strtch th bds ftr tht
- Wtch mvs
- Wt tsd th rl's drmtry
- t hr wrkplc t wt hr t ff dty
- Rd tsd th cmps
- Wtch xhbts sh hs trsts
##### Sy LV Y vry dy vry mmt
##### t FST, FST, FST f r w
- Mk ... r... Prdc LV
可以看到不管處理沒處理的行都打印出來了。
如果使用p
和-n
的組合:
lgl@pArch ~/tmp/sed $ sed -n '/S/s/-/#####/gp' DreamList.puppy
##### S th srs
##### Slf#####stdy MB
##### St sd th lk f r rd d tlk dr sst
##### R th plyrd d Strtch th bds ftr tht
##### Sy LV Y vry dy vry mmt
##### t FST, FST, FST f r w
可以看到只有被處理的行被顯示了。
命令和flags
剛剛說到s
命令最後可以有flags,其中flags和命令很多都是一模一樣的。這裏提一下防止搞暈。
例如:
sed '2,3p' fileName
爲打印2~3行,這裏的p
是和s
同級別的命令。
sed '2,3s/hi/hello/gp'
,這裏的p
屬於s
命令的flags,是s
命令的一部分。
連續使用sed
連續使用sed命令需要使用-e
參數:
sed -e [command1] -e [commaned2]... [inputfile]
如sed -i -e G -e 's/^-/#####/g' fileName
爲先在每一行後面添加空行,再將所有開頭的’-‘替換爲’#####’。
其實連續使用sed還可以使用;
。以上命令等價於:
sed -i '{G;s/^-/#####/g}' fileName
或者sed -i 'G;s/^-/#####/g' fileName
。
奇偶行與n
上面我們說範圍匹配的時候提到過奇偶行。
sed '1~2d' file #刪除奇數行
sed '1~2!d' file #刪除偶數行
sed '2~2d' file #刪除偶數行
也可以用n
(不是-n
)實現同樣的效果:
sed 'n;d' file
刪除偶數行
sed '1d;n;d' file
刪除奇數行
緩衝區
想明白上面的n
和G
的用法,需要涉及到緩衝區。
sed處理問本行的步驟:
- 讀取一行到模式空間緩衝區
- 按照命令對模式空間緩衝區內容進行處理
- 打印處理後緩衝區中的文本
- 循環到第一步讀取下一行,直到文本處理完
臨時緩衝區、n
每一行都被保存在一個叫模式空間的臨時緩衝區中,除非行被刪除或者輸出被取消,否則所有被處理的行都將打印在屏幕上,接着模式空間被清空。
所以sed 'n;d' file
刪除偶數行,可以解釋爲:sed開始讀了第一行到緩衝區,然後執行n
,意味再讀一行,所以就讀了第二行,然後執行d
,於是第二行被刪除了。以此類推,所有的偶數行都被刪除了。
n N Read/append the next line of input into the pattern space.
保持緩衝區、h
、G
h
和g
是互逆的過程,其中消協代表拷貝,大寫代表添加。
h H Copy/append pattern space to hold space.
g G Copy/append hold space to pattern space.
我們先來看一個命令:
sed -e '/test/h' -e '$G' file
在這個例子裏,匹配test的行被找到後,將存入模式空間(臨時緩衝區),h
命令將其複製並存入一個稱爲保持緩存區的特殊緩衝區內,保持緩衝區如果不使用G
取出,將會一直存在(所以叫“保持”緩衝區,而不是“臨時”緩衝區)。
第二條語句的意思是,當到達最後一行後,G
命令取出保持緩衝區的行,然後把它放回模式空間中,且追加到現在已經存在於模式空間中的行的末尾。在這個例子中就是追加到最後一行。簡單來說,任何包含test的行都被複制並追加到該文件的末尾。
看了這我們就知道爲什麼sed G fileName
能夠在每一行後面添加一個空行了。因爲保持緩衝區中沒有內容,G
就只能取到空內容。每次sed取一行文本到臨時緩衝區,G
就在保持緩衝區中取空內容添加到這一行後面,也就是在這行後面添加了一個空行,以此類推,所有行後面都被追加了一個空行。
x
x Exchange the contents of the hold and pattern spaces.
這個時候我們再看x
命令就不難理解了。x
表示互換臨時緩衝區和保持緩衝區的內容。
所以看這個命令:
lgl@pArch ~ $ seq 3
1
2
3
lgl@pArch ~ $ seq 5 | sed 'x'
1
2
3
4
lgl@pArch ~ $
剛開始hold space是空的,1和空行交換,1進入了hold space,空行進入了pattern space,輸出空行,接下來1和2交換,2進入hold space,1換到了pattern space,輸出。一直到最後,5被換如了hold space。所以只打印了空行和1~4。