linux小記:恍然大明白,sed命令中[commands]的格式

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的行”。

複雜的指令和之前的範圍組合起來就不容易看懂了——
比如ss是個自帶一大串參數的指令,也是用的最多的指令。其格式爲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命令改壞了源文件欲哭無淚強!

-np

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刪除奇數行

緩衝區

想明白上面的nG的用法,需要涉及到緩衝區。

sed處理問本行的步驟:

  • 讀取一行到模式空間緩衝區
  • 按照命令對模式空間緩衝區內容進行處理
  • 打印處理後緩衝區中的文本
  • 循環到第一步讀取下一行,直到文本處理完

臨時緩衝區、n

每一行都被保存在一個叫模式空間的臨時緩衝區中,除非行被刪除或者輸出被取消,否則所有被處理的行都將打印在屏幕上,接着模式空間被清空。

所以sed 'n;d' file刪除偶數行,可以解釋爲:sed開始讀了第一行到緩衝區,然後執行n,意味再讀一行,所以就讀了第二行,然後執行d,於是第二行被刪除了。以此類推,所有的偶數行都被刪除了。

n N Read/append the next line of input into the pattern space.

保持緩衝區、hG

hg是互逆的過程,其中消協代表拷貝,大寫代表添加。

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章