Sed and awk 筆記之 sed 篇:模式空間與地址匹配

模式空間

在上一篇Sed&awk筆記之sed篇:簡單介紹中,我們曾經介紹過簡單的sed處理流程,這裏首先回顧下:

  1. 讀入新的一行內容到緩存空間;
  2. 從指定的操作指令中取出第一條指令,判斷是否匹配pattern;
  3. 如果不匹配,則忽略後續的編輯命令,回到第2步繼續取出下一條指令;
  4. 如果匹配,則針對緩存的行執行後續的編輯命令;完成後,回到第2步繼續取出下一條指令;
  5. 當所有指令都應用之後,輸出緩存行的內容;回到第1步繼續讀入下一行內容;
  6. 當所有行都處理完之後,結束;

由此可見,sed並非是將一個編輯命令分別應用到每一行,然後再取下一個編輯命令。恰恰相反,sed是以行的方式來處理的。另外一方面,每一行都是被讀入到一塊緩存空間,該空間名爲模式空間(pattern space),這是一個很重要的概念,在後文中會多次被提及。因此sed操作的都是最初行的拷貝,同時後續的編輯命令都是應用到前面的命令編輯後輸出的結果,所以編輯命令之間的順序就顯得格外重要。

簡單例子

讓我們來看一個非常簡單的例子,將一段文本中的pig替換成cow,並且將cow替換成horse:

 

$ sed 's/pig/cow/;s/cow/hores/' input

初看起來好像沒有問題,但是實際上是錯誤的,原因是第一個替換命令將所有的pig都替換成cow,緊接着的替換命令是基於前一個結果處理的,將所有的cow都替換成horse,造成的結果是全部的pig/cow都被替換成了horse,這樣違背了我們的初衷。

在這種情況下,只需要調換下兩個編輯命令的順序:

$ sed 's/cow/hores/;s/pig/cow/' input

模式空間的轉換

sed只會緩存一行的內容在模式空間,這樣的好處是sed可以處理大文件而不會有任何問題,不像一些編輯器因爲要一次性載入文件的一大塊內容到緩存中而導致內存不足。下面用一個簡單的例子來講解模式空間的轉換過程,如下圖所示:

模式空間的轉換

現在要把一段文本中的Unix System與UNIX System都要統一替換成The UNIX Operating System,因此我們用兩句替換命令來完成這個目的:

s/Unix /UNIX /
s/UNIX System/UNIX Operating System/

對應上圖,過程如下:

  1. 首先一行內容The Unix System被讀入模式空間;
  2. 應用第一條替換命令將Unix替換成UNIX;
  3. 現在模式空間的內容變成The UNIX System;
  4. 應用第二條替換命令將UNIX System替換成UNIX Operating System;
  5. 現在模式空間的內容變成The UNIX Operating System;
  6. 所有編輯命令執行完畢,默認輸出模式空間中的行;

地址匹配

地址匹配在sed中是非常重要的一塊內容,因爲它限制sed的編輯命令到底應用在哪些行上。默認情況下,sed是全局匹配的,即對所有輸入行都應用指定的編輯命令,這是因爲sed依次讀入每一行,每一行都會成爲當前行並被處理,所以s/CA/California/g會將所有輸入行的CA替換成California。這一點跟vi/vim是不一樣的,衆所周知,vim的替換命令默認是替換當前行的內容,除非你指定%s纔會作全局替換。

可以通過指定地址來限制sed的處理範圍,例如只想將替換包含Sebastopol的行:

Sebastopol/s/CA/California/

/Sebastopol/是一個正則表達式匹配包含Sebastopol的行,因此像行“San Francisco, CA”則不會被替換。

sed命令中可以包含0個、1個或者2個地址(地址對),地址可以爲正則表達式(如/Sebastopol/),行號或者特殊的行符號(如$表示最後一行):

  • 如果沒有指定地址,默認將編輯命令應用到所有行;
  • 如果指定一個地址,只將編輯命令應用到匹配該地址的行;
  • 如果指定一個地址對(addr1,addr2),則將編輯命令應用到地址對中的所有行(包括起始和結束);
  • 如果地址後面有一個感嘆號(!),則將編輯命令應用到不匹配該地址的所有行;

爲了方便理解上述內容,我們以刪除命令(d)爲例,默認不指定地址將會刪除所有行:

$ sed 'd' list 
$ 

指定地址則刪除匹配的行,如刪除第一行:

$ sed '1d' list 
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

或者刪除最後一行,$符號在這裏表示最後一行,這點要下正則表達式中的含義區別開來:

$ sed '$d' list 
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

這裏通過指定行號刪除,行號是sed命令內部維護的一個計數變量,該變量只有一個,並且在多個文件輸入的情況下也不會被重置。

前面都是以行號來指定地址,也可以通過正則表達式來指定地址,如刪除包含MA的行:

$ sed '/MA/d' list 
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

通過指定地址對可以刪除該範圍內的所有行,例如刪除第3行到最後一行:

$ sed '2,$d' list 
John Daggett, 341 King Road, Plymouth MA

使用正則匹配,刪除從包含Alice的行開始到包含Hubert的行結束的所有行:

$ sed '/Alice/,/Hubert/d' list 
John Daggett, 341 King Road, Plymouth MA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

當然,行號和地址對是可以混用的:

$ sed '2,/Hubert/d' list 
John Daggett, 341 King Road, Plymouth MA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

如果在地址後面指定感嘆號(!),則會將命令應用到不匹配該地址的行:

$ sed '2,/Hubert/!d' list 
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA

以上介紹的都是最基本的地址匹配形式,GNU Sed(也是我們用的sed版本)基於此添加了幾個擴展的形式,具體可以看man手冊,或者可以看我之前寫的Sed命令地址匹配問題總結一文。

上面說的內容都是對匹配的地址執行單個命令,如果要執行多個編輯命令要怎麼辦?sed中可以用{}來組合命令,就好比編程語言中的語句塊,例如:

$ sed -n '1,4{s/ MA/, Massachusetts/;s/ PA/, Pennsylvania/;p}' list 
John Daggett, 341 King Road, Plymouth, Massachusetts
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls, Pennsylvania

這一篇的內容就到此爲止,下一篇見Sed&awk筆記之sed篇:基礎命令

轉載請註明轉自: 糰子的小窩 , 本文固定鏈接: Sed and awk 筆記之 sed 篇:模式空間與地址匹配

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