Sed and awk 筆記之 sed 篇:簡單介紹

最近在閱讀《sed & awk》,這本書是sed和awk相關書籍中比較經典的一本。我在讀書的時候有一個習慣,就是會作一些筆記,如果有條件我會放到博客中。寫博客不僅是給別人看的,更是寫給自己看的,同時因爲寫給別人看,所以必然會在一些細節的地方寫得很清楚明瞭,可以加深自己對原書的理解,同時以後回頭看的時候,我自己也能快速的回憶起來。

另外一方面,我會選擇英文原版來閱讀而非中文翻譯版,主要是出於英文版的內容更加準確、容易領會作者的本意這個方面的原因。畢竟翻譯的內容一方面因爲翻譯的時候會丟失一些原版的意思,同時因爲不同的人有不同的理解,在翻譯中可能會夾雜着自己個人的理解。就好比這一系列的文章,許多內容都是出自原書,我只不過是翻譯了些內容加了點註解而已,所心也只能稱之爲筆記。

文中對一些術語的翻譯只是按本人自己的喜好而定,請見諒。

 

本系列包含兩部分的內容:sed篇和awk篇。

sed篇總共分成6章:

awk篇暫時還未計劃。

Sed是什麼

《sed and awk》一書中(1.2 A Stream Editor)是這樣解釋的:

Sed is a "non-interactive" stream-oriented editor. It is stream-oriented because, like many UNIX
programs, input flows through the program and is directed to standard output.

Sed本質上是一個編輯器,但是它是非交互式的,這點與VIM不同;同時它又是面向字符流的,輸入的字符流經過Sed的處理後輸出。這兩個特性使得Sed成爲命令行下面非常有用的一個處理工具。

如果對sed的歷史有興趣,可以看書中2.1節"Awk, by Sed and Grep, out of Ed",這裏就不多介紹。

基本概念

sed命令的語法如下所示:

sed [options] script filename

同大多數Linux命令一樣,sed也是從stdin中讀取輸入,並且將輸出寫到stdout,但是當filename被指定時,則會從指定的文件中獲取輸入,輸出可以重定向到文件中,但是需要注意的是,該文件絕對不能與輸入的文件相同。

options是指sed的命令行參數,這一塊並不是重點,參數也不多。

script是指需要對輸入執行的一個或者多個操作指令(instruction),sed會依次讀取輸入文件的每一行到緩存中並應用script中指定的操作指令,因此而帶來的變化並不會影響最初的文件(注:如果使用sed時指定-i參數則會影響最初的文件)。如果操作指令很多,爲了不影響可讀性,可以將其寫到文件中,並通過-f參數指定scriptfile:

sed -f scriptfile filename

這裏有一個建議,在命令行中指定的操作指令最好用單引號引起來,這樣可以避免shell對特殊字符的處理(如空格、$等,關於這一點可以參考簡潔的Bash編程技巧續篇 - 9. 引號之間的區別)。這個建議同樣適用grep/awk等命令,當然如果有時候確實不適合使用單引號時,記得對特殊字符轉義。

無論是將操作指令通過命令行指定,還是寫入到文件中作爲一個sed腳本,必須包含至少一個指令,否則用sed就沒有意義了。一般會同時指定多個操作指令,這時候指令之間的順序就顯得非常重要。而你的腦海中必須有這麼一個概念,即每個指令應用後,當前輸入的行會變成什麼樣子。要做到這一點首先必須要瞭解sed的工作原理,要做到“知其然,且知其所以然”。

每條操作指令由pattern和procedure兩部分組成,pattern一般是用'/'分隔的正則表達式(在sed中也有可能是行號,具體參見Sed命令地址匹配問題總結),而procedure則是一連串編輯命令(action)。

sed的處理流程,簡化後是這樣的:

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

具體流程見下圖:
simple sed process flow chart

簡單例子

概念如果脫離實際的例子就會顯得非常枯燥無趣,這本書在這一點上處理得很好,都是配上實際的例子來講解的。不過有點遺憾的是,書中的例子大多是與troff macro有關的,我們很難有條件來實際測試例子,在筆記中我會盡量使用更加淺顯易懂的例子來說明。

下面是摘自書中的一個例子,假設有個文件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
Sal Carpenter, 73 6th Street, Boston MA

這個例子中用到的知識點都會在後文中介紹,你可以先知道有這個東西就行。

假如,現在打算將MA替換成Massachusetts,可以使用替換命令s:

$ sed -e 's/MA/Massachusetts/' 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 PA
Eric Adams, 20 Post Road, Sudbury Massachusetts
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston Massachusetts

這裏面的-e選項是可選的,這個參數只是在命令行中同時指定多個操作指令時才需要用到,如:

$ sed -e 's/ MA/, Massachusetts/' -e 's/ PA/, Pennsylvania/' 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
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston, Massachusetts

即使在多個操作指令的情況下,-e參數也不是必需的,我一般不會加-e參數,比如上面的例子可以換成下面的寫法:

$ sed 's/ MA/, Massachusetts/;s/ PA/, Pennsylvania/' list 

操作指令之間可以用分號分隔,這點和shell命令可以用分號分隔是一樣的。

這裏提前說一點使用s替換命令需要小心的地方,不要忘記結尾的斜槓,如果你遇到下面的錯誤說明你犯了這個錯誤:

$ sed -e 's/ MA/, Massachusetts' list
sed: -e expression #1, char 21: unterminated `s' command

假如這時,我只想輸出替換命令影響的行,而不想輸出文件的所有內容,需要怎麼辦?這個時候可以利用替換命令s的p選項,它會將替換後的內容打印到標準輸出。但是僅僅這樣是不夠的,否則輸出的結果並非我們所想要的:

$ sed -e 's/ MA/, Massachusetts/p' list
John Daggett, 341 King Road, Plymouth, Massachusetts
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 PA
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston, Massachusetts
Sal Carpenter, 73 6th Street, Boston, Massachusetts

從上面可以看出,文件的所有行都被打印到標準輸出,並且被替換命令修改的行輸出了兩遍。造成這個問題的原因是,sed命令應用完所有指定之後默認會將當前行打印輸出,所以就有了上面的結果。解決方法是在使用sed命令是指定-n參數,該參數會抑制sed默認的輸出:

$ sed -n 's/ MA/, Massachusetts/p' list
John Daggett, 341 King Road, Plymouth, Massachusetts
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Sal Carpenter, 73 6th Street, Boston, Massachusetts

正則表達式

書中的第3章的內容都是介紹正則表達式,這是因爲sed的很多地方都需要用到正則表達式,比如操作指令的pattern部分以及替換命令中用到的匹配部分。關於正則這部分可以看Linux/Unix工具與正則表達式的POSIX規範,文章中已經講得非常清楚了,我自已在忘記一些內容的時候也是去查閱這篇文章。

由於篇幅限制,本篇就到此爲止,下一篇見Sed&awk筆記之sed篇:模式空間與地址匹配

轉載請註明轉自: 糰子的小窩 , 本文固定鏈接: Sed and awk 筆記之 sed 篇:簡單介紹

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