1、Sed,What? c++
在Gnu的介紹Sed的文檔中的第一句話就是「Sed is a stream editor」,流編輯器,關於什麼時流編輯器,讀者能夠Google一下,或者參考經典的流編輯器ed。這裏提供一本學習Sed和AWK的神書 Dale Dougberty & Arnold Robbins的《Sed & AWK》。一句話概況就是「對輸入流進行一系列的基本文本轉換」,該句話同時概況了Sed的工做方式:讀入一個輸入流,而後對輸入做一系列的操做,再將其輸出。 正則表達式
2、Sed’s Usage shell
因爲筆者的工做環境是Linux平臺,所以筆者主要使用Gnu版本的Sed,可是我還未使用過Gnu對Sed擴展的部分,故此不做介紹。若是考慮跨平臺,那麼最好就是以Sed出世時的文章Lee E.McMahon,的「SED--A Non-interactive Text Editor」來學習。我主要以Ken Pizzini and Paolo Bonzini的《Sed--A Stream Editor》即Gnu的Sed文檔來學習。 編程
Gnu對使用Sed的描述爲: 編程語言
sed OPTIONS ... [SCRIPT] [INPUTFILE...] 編輯器
從上面能夠看到其格式符合通常Linux命令,其中SCRIPT有兩種方式給出: 學習
1)「-f sctipt_file」將sed命令放在腳步文件script_file中,而後用「-f」選項指出 spa
2)「-e ‘command_0;command_1...’」 用」-e」選項指出由單引號包裹的命令,各命令之間能夠用分號或者換行隔開。這裏推薦用單引號,不少經典中都這樣推薦,我也套用了,好像是說防止與shell腳本之間雙引號之間的轉義問題,我沒有遇到過。 code
若是沒有給出INPUTFILE或者其爲「--」,那麼輸入文本將從標準輸入中取得。INPUTFILE能夠是多個文件的羅列,這樣Sed會依次取出文件中的每一行,直到最後一個文件的最後一行,即遇到了EOF。 regexp
OPTIONS有多個選擇,這裏我根據個人使用列出幾個我常使用的選項,關於選項的詳細介紹能夠參見《Sed--A Stream Editor》。
1)-n :根據Sed的工做方式,Sed在對一行文本作完一系列的操做後會將patter(見下面的「Sed!How to work?」)裏面的內容打印到標準輸出,該選項能夠抑制該功能,從而只有顯示的指定了「p」打印命令纔會將處理結果打印出來。
2)-e : 在上面的SCRIPT部分咱們已經介紹了它的使用方法了,該選項後接單引號包圍的Sed命令。
3)-i [SUFFIX] :根據Sed的工做方式,Sed並不會修改源文本內容,而僅僅讀入其內容,而後做處理後輸出到標準輸出,也能夠經過重定向到某個文件。若是但願修改源文件,那麼就可使用該命令,其中可選的「SUFFIX」部分是指,若是有該部分,那麼Sed在處理的過程當中會把每行數據逐一的輸入到「源文件名+SUFFIX」的backup文件中。Sed的工做方式是這樣的,若是指定SUFFIX,就會在處理完輸入流,將該backup文件重命名爲源文件名,這樣就沒有了backup文件,感受是僅修改了源文件;若是給出了SUFFIX選項,那麼最後會將其複製一份並命名爲源文件名,這樣感受就是修改源文件的同時產生了backup文件。
4)-s :在Sed使用格式裏面咱們說到INPUTFILE能夠是多個文件的羅列,而對於流編輯器來講都有個行數的概念,好比ed中要修改某行能夠經過行號定位到改行,在Sed中也能夠經過行號來選擇某行。默認狀況下,Sed時將全部文件當成一個大的文件流的,並對他們進行統一計數,即第二個文件的首行是第一個文件末行號加一。經過該選項,可使行號是每一個文件獨立的。即第一個文件的首行行號爲1,第二個文件的首行行號也是1。
3、Sed!How to work?
Sed是如何處理輸入流的呢?在說明這個以前首先要介紹Sed維護的兩個數據區,Sed經過這兩個數據區來完成對輸入流的一系列操做。一個叫「pattern space」一個叫「hold space」。
咱們能夠用路邊的停車位來舉個例子,pattern咱們能夠認爲他就是停車位前面的道路部分,他裏面的數據就是Sed命令要處理的數據;hold就是停車位,其至關於一個數據的暫存區。在道路(pattern)上跑的車子能夠隨時進入到停車位(hold)中,而停車位(hold)中的的車子也能夠隨時出來到跑道(pattern)上。一般狀況下咱們認爲車道上和停車位裏面僅能容納一輛車,即一行數據。
Sed在工做時,首先從輸入流中讀入一行文本,而後將文本末尾的換行符去掉後的內容放到pattern中,以後對pattern中的內容依次執行給出的Sed命令,其中的命令是逐一原地執行。好比首先執行第一條命令,而後將結果放回pattern裏面,在對處理的結果執行第二條命令。這樣就會致使若是第一條命令將內容改變了,那麼緊接着的第二條輸出命令就會輸出改變後的內容,而不是本來讀入時候pattern裏的內容。在依次執行完了全部的命令後,Sed將pattern裏面的內容取出並在末尾加上換行將其輸出到標準輸出(這就是默認狀況下Sed會輸出操做結果的原理,可使用「-n」選項進行抑制。)。
從圖中能夠了解Sed工做時各個部分所作的事情,其中Hold部分後面會結合Sed命令(h、H、g、G、X)來介紹。輸入/輸出主要就是一個換行符的增刪操做。這裏重點說下Sed命令的構成。
Sed的命令有一個可選的地址或者地址範圍加上一個一個字母的命令和該命令須要的選項構成,這裏說的地址或者地址範圍就是上面討論的對於流編輯器特有的性質:先指定操做的位置,再進行操做。這樣的步驟來選擇操做範圍的做用。其有多種指定方式:行號、數字推導公式、正則表達式等。而命令的組成,在《Sed & AWK》中說Sed的命令集是由25個命令組成的,見下面的Sed’s Commands。這裏先來看幾個命令的示例:
# 表示註釋 2 d # 將輸入流中的第二行刪除 /[0-9]\+/p # 打印包含數字的行的內容 s/go/come/g # 將輸入流中每一行中的全部單詞「go」替換成「come」
上面的示例分別用了行號和正則表達式來選擇地址,若是沒有給定地址則選擇每一個輸入行。
若是對一個地址有多個操做,那麼能夠將命令羅列在「{}」中,命令之間用分號進行分割。也用換行進行分割,此時要求第一個「{」跟在命令以後,第一條命令能夠與其同行或者在其後面的一行,可是結尾的「}」必須單獨位於一行。
4、Sed’s Address
從上面能夠知道要想對輸入流進行操做,就必須先指定命令實用的地址範圍,若是不指定,Sed與其餘流編輯器不一樣的在於他會選擇全部的輸入行。地址的選擇有多種方式,我經常使用的概括有三種:行號、數字推導公式、正則表達式
1)行號: 直接用數字便可給出行號,如上面的 `2 d`中的2表示第二行,個人習慣是再行號和命令之間用一個空格隔開,這樣感受比較舒服些,不知道你怎麼想。呵呵。這裏要注意的就是若是在使用Sed時用了「-s」或者「-i」選項,那麼行號時每一個文件獨立的,不然默認狀況下時全部輸入文件當作一個大的輸入流進行計數。
2)數字推導公式: 數字推導公式也是基於行號的,所以也要注意「-s」或者「-i」選項。他經過公式來選擇符合公式的行號。有兩個公式:
a. first~step: 會選擇行號s = first+step * n (n>=0的整數)的行
b. addr1+/- ,N:會選擇行號從addr一、addr1+/-1...到addr1+/-N的行,是一個範圍
3)正則表達式 :正則表達的內容這裏就不贅述了,須要注意的時Gnu的和腳步語言如Python、Javascript裏的書寫方式有點不一樣(「+」用「\+」來表示,具體能夠參考《Sed--A Stream Editor》或者《Sed & AWK》第三章進行詳細學習。)。正則表達式的格式有兩種:
a. /regexp/
b. /%regexp%
其中在第一種方式中,若是正則表達式中有「/」符,須要用「//」進行轉義。而第二種方式能夠減小正則表達式中包含不少/時要轉義的操做。
若是在上面兩種方式的後面加上大寫的「I」那麼在匹配時會忽略正則表達式的大小寫的區別。這時和行號選擇同樣,我傾向於在地址和命令之間加上一個空格。如:
/[0-9]\+/ p
在數字推導裏面咱們已經看到了一種指定一個地址範圍的方式了,就是Gnu擴展的「addr1+/- ,N」格式,其實正則表達式也能夠指定多個行,若是想用行號指定多個行,則用逗號將兩個行號分開便可。如:
num1,num2 # 選中從num1到num2的全部行
若是後者行號小於前者,那麼僅選中前面的一個行號。
這裏也能夠把行號替換成正則表達式,
regexp1,regexp2
對於正則表達式則是從第一個匹配regexp1的行到最後一個匹配regexp2的全部的行。
5、Sed’s Commands
《Sed & AWK中說》Sed名集有25個命令,這裏做分類進行介紹。
命令 |
備註 |
# |
註釋 |
s |
替換 |
p P n N d D |
打印 下一個輸入 刪除 |
y ! = q |
|
h g X H G |
pattern放入hold hold 放入 pattern 交換hold和pattern |
b : t |
循環 |
r w |
讀寫文件 |
c i a |
不是美國的cia,而是修改、插入、追加 |
1)#
如同c++裏面的「//」做爲行註釋使用,有的實現中僅能夠在腳本的開始出進行註釋,可是在Gnu中是能夠在任何地方進行註釋的,並且,若是腳本文件的首行時是「#n」那麼其相對於再調用Sed的時候使用了「-n」選項抑制自動輸出功能。
2)強大的「s」命令
使用Sed的使用者,十有八九要用到該命令,甚至不管是再Gnu的文檔仍是最開始介紹Sed的文檔中都將「s」命令單獨做爲一個章節來說解,可見其重要性。
「s」命令的完整格式爲:
s/regexp/replacement/flags
其中/能夠替換成其餘符號從而避免和正則表達式中的「\」衝突。好比能夠替換成
s#regexp#replacement#flags
s!regexp!replacement!flags
可是符號必須成對。
「s」命令最基本的功能就是找到regexp匹配的部分,並用replacement替換該部分,因爲替換的做用的重要性,Sed又提供了衆多的flags來加強其用處。
對於repalcement,其中能夠出現幾個元字符。」\n」表示該處用第n(n爲1到9的整數)個regexp中的\符號對包裹的正則表達式匹配的數據內容進行填充,「&」能夠表示整個regexp匹配的數據內容進行填充。如:
s/\(Sed\)/Gnu \1/
將「(Sed)」轉換成「Gnu (Sed)」
其flags能夠將多個flag連在一塊兒使用。
「g」:默認狀況下,s只會替換第一個匹配regexp的部分,該flag會替換全部匹配的部分。
「n」: 默認狀況下時第一個,g表示全部,n爲一個正整數,表示第幾回出現的被替換。
「p」:不等該行處理完或者遇到p命令就打印出pattern裏面的內容。
「w filename」:再替換完成後,將patern內容輸出到文件 filename中。
「i」:Gnu對其的擴展,使得在查找替換時不區分大小寫與regexp匹配。
3)pnd
「p」表示「print」;「n」表示「next」,「d」表示「delete」
「p」命令會將pattern裏面的全部內容打印到標準輸出,一般該命令是和「-n」搭配使用的,抑制全部結果向標準輸出僅按命令要求,輸出須要輸出的內容。
「n」命令將當前pattern裏的內容打印到標出輸出,若是使用了「-n」選項則不進行打印。同時從輸入流取得下一行替換pattern中的內容,並對其執行「n」以後的命令,當某此「n」從輸入流取不到下一行時,Sed忽略後面的命令並退出。好比命令
sed -n -e ‘ n; p’ data_file
會打印偶數行的內容。
「d」命令會刪除pattern裏面的內容,並無論後面的命令,從新開始新一輪的從輸入流中取新行放到pattern而後依次執行命令的過程,有點相似與循環中的continue。
sed -n -e ‘/[0-9]|+/d;p’
會打印出全部不包含數字的行。
至於大寫PND,就要擴展咱們pattern放一行數據的限制了,pattern其實是能夠放多行數據的,只是經常使用的狀況下僅放一行數據。
「P」和「p」命令同樣,只是會將pattern裏面的第一個換行以前的內容都打印到標準輸出。
「D」和「d」命令同樣,只是將pattern裏面的第一個換行以前的內容(包括換行),若是pattern裏還有內容,那麼對其從新執行全部命令,若是此時pattern爲空那麼開始新的循環。
「N」命令先向pattern裏面添加一個換行符,而後從輸入流中取得一個新行放入pattern中,若是取不到,那麼直接退出而無論後面的命令。好比命令
sed -n -e ‘1 {N;N;N;p}’ data_file
會打印前4行內容,而命令:
sed -n -e ‘1 {N;N;N}; P; D’ data_file
會依次打印全部行的內容。
4)y!=q
爲了方便,將這四個命令這樣寫,y不等於q。其中「!」,「=」也是兩個獨立的命令。
「y」命令,其格式爲:
y/source-charts/dest-chars/
其中source-charts與dest-chars中的字母要同樣多。Sed會將pattern裏面全部出如今source-charts裏面的字母替換成dest-chars裏面對於順序的字母。其中有兩個典型的例子。
a.將全部字母轉換成大寫字母:
y/abcdefghiklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ
b.將Tab轉換成空格:
y/tab/ /
「!」命令用在地址後面,而後用{}包裹後續命令,這樣可使得在地址所指範圍以外的全部行都執行{}中的命令。如:
sed -n -e ‘/[0-9]\+/! {p}’ data_file
會打印全部不含數字的行。
「=」命令會打印當前輸入行的行號做爲單獨的一行。如:
sed -n -e ‘/[0-9]\+/! {=;p}’ data_file
會打印不含數字的行的行號做爲一個單獨的行並跟一行其內容。
「q」,Sed遇到該命令會打印當前pattern內容,若是使用了「-n」選項則不打印,而後退出Sed而無論後面的命令,Gnu擴展還能夠在「q」後面增長一個數字做爲Sed的退出碼。注意,該命令的地址部分只能是單行地址而不能夠時地址範圍,亦即不能用正則表達式。
5)h g X H G
這五個命令就要涉及到hold區域了,咱們在舉停車位的例子中將其比喻成停車位。那麼怎麼泊車怎麼從停車位開出去呢?這五個命令就是修改pattern和hold內容的命令了。
「h」命令,將用pattern裏的內容替換hold裏面的內容,可是pattern裏面的內容不改變。而「g」命令則執行相反的操做,用hold裏面的內容替換pattern裏面的內容,hold的內容不改變。
而大寫的「H」命令也是望hold裏面放數據,這時就不能把hold當成只能停一部車的停車位了,他能夠放多個行的數據。它先想hold裏面加入一個換行符,而後將pattern裏面的內容追加到hold裏面去。「G」執行相反的操做,想pattern裏面加入一個換行符,而後將hold裏面的內容追加到pattern裏面。
「X」命令能夠交換pattern和hold裏面的內容。
6)b:t
Gnu說這桑格命令是Sed專家使用的命令,這三個命令有什麼神奇的呢?這三個命令能夠實現跳轉,這樣即可以造成條件與循環的邏輯結構,從而使Sed像一門編程語言同樣,賦予Sed更多能力。
「:」命令設立一個標號。「: label」 能夠設立標號label如同彙編同樣。
「b」命令能夠無條件跳轉到特定的標號處,「b label」,使命令執行流程跳轉到lable處,這個能夠參考彙編裏面的跳轉和標號設置。
「t」命令在判斷當前輸入流中存在成功的替換操做後便會跳轉到其後的label中,
「t label」,當讀入一個新的文件或者執行過t命令後,該條件變爲否。
關於這種循環的控制,用過彙編的可能比較容易理解,Gnu說它屬於高級內容,我也沒有用過幾回,這裏就不舉例了,能夠參考《Sed & AWK》給出的宏替換的命令來學習。
7)cia
這裏的CIA不是美國的cia,而是指修改、插入、追加。使用方法是:
c/i/a\
text
該命令後面加上「\」符,而後接text文本內容,其中a會將text內容在pattern內容輸出後、下一個輸入流內容讀入前輸出到標準輸出。而i會將text內容在pattern內容輸出前將text輸出到標準輸出。而c會刪除pattern內容,而後輸出text內容,並進入到下一個循環。
8)rw不是很經常使用,想了解能夠參考《Sed & AWK》
6、Reference
[1] Lee E.McMahon, SED--A Non-interactive Text Editor, USD
[2] Ken Pizzini and Paolo Bonzini, Sed--A Stream Editor,Gnu Document, 2009
[3] Dale Dougberty and Arnold Robbins,Sed & AWK,2nd Edition, O’Reilly, 1997