摘要:總結sed的功能與特點、基本文法與使用注意事項。最後給出幾個小例子,特別是用於把本文從純文字轉化為CSDN部落格的指令碼例子。
1 sed 概述
sed是Stream EDitor的縮寫。它是一種非互動編輯器,這與互動vim區別非常明顯。
1.1 主要使用領域
- (1) 大量文檔的批處理編輯;
- (2) 把編輯指令嵌入到指令碼中。
1.2 sed的獨特之處
- (1)逐行處理文本,每次只處理一行文本。帶來的好處是無論要處理的檔案有多大,sed只是
- 讀取處理的一行進行處理,佔用記憶體小,速度快;
- (2)完全支援管道,能從標準輸入讀取,寫入標準輸出,適合unix管線處理;
- (3)不改變源檔案,所有編輯命令操作的都是模式空間,產生的結果輸出到標準輸出中,無需擔心破壞源檔案。
1.3 sed的一般工作流程
- (1)從輸入讀取第一行到模式空間;
- (2)判斷當前當前行是否滿足行約束,如果不滿足,則轉到(4);
- (3)對模式空間進行編輯操作;
- (4)列印模式空間內容到標準輸出;
- (5)判斷當前行是否是最後一行,如果是,則轉到(7);
- (6)清空模式空間,並讀取下一行,轉到(2); ** 保持空間不會清除 **
- (7)結束.
需要注意的是,行約束只是判斷編輯的準則,不滿足行約束的行也會被sed自動讀取到模式空間,因為sed會從頭到尾處理輸入的--所有行--(例外:q命令會導致sed提前退出)。
2 sed的行約束
sed的基本文法格式非常簡潔,就是:行約束加上編輯命令,如果不指定行約束,則預設所有行都滿足。其中行約束有兩種形式,一是單獨行約束,二是連續塊約束。幾個例子:
- (1) sed -e 'd' myfile 所有行都滿足行約束
- (2) sed -e '11d' myfile 只有第11行滿足行約束
- (3) sed -e '11,23d' myfile 11,12,...,23,這些行滿足行約束
- (4) sed -e '/^[a-z]/d' myfile 匹配Regex^[a-z]的行滿足行約束
- (5) sed -e '/regex1/,/regex2/d' myfile 第一個匹配regex1的行滿足,後續行都滿足,直到第一個滿足regex2的行,再往後的行不滿足。
判斷流程是:
- (a)當前行是否滿足regex1,如果不滿足,讀取下一行,轉到(a);
- (b)進行編輯操作;判斷當前行是否滿足regex2,如果滿足則後續所有行都不再滿足;否則轉到(b)。
據此可以推出: 3,1表示從3,...都滿足,因為第三行後面不可能再出現第一行; 3,3表示只有第三行滿足。
可見,使用,來表示連續塊。另外,sed支援行約束的嵌套,如下:
/regex1/{操作1/regex2/操作2操作3}
此時只用同時匹配regex1和regex2的行才滿足行約束。最後需要說的是,sed還支援取反約束!,如$!表示除了最後一行之外的所有行都滿足。
3 sed 基本命令
在詳細講解編輯命令之前,需要首先提醒大家,由於sed的多個編輯命令作用於同一個模式空間,所以命令的順序非常重要。相同的一組命令,順序不同,結果可能完全不同。
3.1 =、p、d
p輸出模式空間內容對應的行號,p則輸出模式空間的內容,d是刪除模式空間內容,執行流結束。
3.2 s
這可能是最重要的sed編輯命令了,用於替換文本。文法格式為 s/要匹配的正則/新內容/。預設只會替換掉一行中第一個匹配的內容,如果要替換行中所有匹配的地方,則使用s/正則/新內容/g。其實這裡最重要的是Regex的知識,這與grep裡的正則完全相同。需要注意的是,分隔字元/可以是任一字元,再就是"新內容"部分也有自己特定的元字元(如&),這與正則的元字元不同。
3.3 i、a、c
i用於在當前模式空間的上面插入一行或多行、a類似i只是在模式空間下面插入、c則是替換模式空間的內容。這組命令的特定是需要其文法格式是多行,所以基本都寫在sed指令檔中。如:i\在當前行下面插入的第一行\在當前行下面插入的第二行
3.4 n
前面我們的討論中,sed的執行流都是先讀取一行到模式空間,然後執行編輯,再輸出, 當前行完全處理後sed首先清空模式空間,然後自動再讀取下一行到模式空間中。其實在執行編輯的過程中也可以通過命令n(ext)來讀取檔案的下一行,此時模式空間裡的內容被下一行完全替換,並且當前的行號增1,但是控制流程沒有回到起始點,而是在n(ext)命令後繼續。例如如下命令會列印檔案的偶數行:
sed -n -e 'n;p'
3.5 r、R、w、W
這四個命令用於讀寫外部檔案,應用較少,暫且不說。
4 sed 進階命令4.1 保持空間與模式空間 N、G、H、g、h、x、D、P
模式空間在前面已經說過了,但是前面我們的例子中,模式空間裡都只存在一行文本。其實模式空間不局限於一行,可以有多行。除模式空間外,sed還有一個輔助的記憶體空間,叫做保持空間。有了這兩個記憶體空間,sed可以說是無所不能。
前面我們使用過了n命令,作用是讀取下一行到模式空間,替換掉當前模式空間的內容。其大寫字元N則不同,雖都是讀取下一行內容,但是N會把讀取的內容附加到當前模式空間內容的後面,而不是替換。g用於讀取保持空間內容到模式空間,h則相反,大寫版本的命令不是覆蓋而是附加,x則用於交換模式空間和保持空間的內容。d是刪除整個模式空間的內容,而D則是刪除模式空間中的第一行,兩者都會導致執行流重新回到最開始,但是如果模式空間不為空白,sed不會讀取輸入的下一行。 P與p對應,列印的是模式空間的第一行。
4.3 執行流 b、t
正常情況下,sed的一組命令就是按照書寫的先後順序執行,為了一些進階的編輯,sed提供了改變順序執行流的特殊命令。其文法格式與C語言非常類似。b是branch的縮寫,t是test的縮寫。 b代表無條件轉移,t表示條件轉移,依據的是上次t之後最近一次s命令的替換成功與否。
編輯操作1:mylabel編輯操作2[address]t mylabel
5 注意事項5.1 不同環境的元字元定義
sed的行號約束使用$表示最後一行,而正則中$則表示行尾位置,s命令的新內容部分也有&等自己的特殊元字元。如果再加上shell本身的元字元則情況更加複雜,所以一定要注意元字元的使用環境。
6 實用的sed指令碼6.1 Unix分行符號Windows分行符號相互轉化
sed -e 's/$/\r/' myunix.txt > mydos.txtsed -e 's/.$//' mydos.txt > myunix.txt
6.2 行序反轉
該功能類似於tac命令
sed -e '1!G;h;$!d' forward.txt > backword.txt
6.3 把純文字轉化為樣式自訂的部落格文章
在CSDN上寫部落格,其內建的編輯器用的很不爽,而且CSDN部落格系統也不支援使用者自訂樣式。這讓使用者情何以堪!我本人喜歡用vim純文字方式寫東西,所以想到了使用sed這個強大的編輯器來把純文字轉為自訂樣式的CSDN部落格。純文字格式的規定是:段落開頭必須空2格,標題定格寫以1,1.1,1.1.1的形式定義,列表以(1),(2),...或(a),(b),...來定義。各結構之間使用空行分隔開。使用:nochange和nochange:包圍的文本原樣輸出,本文自身就是有下面的sed指令碼自動產生的。在進行處理之前,先進行安全html編碼處理,防止原文中有和html衝突的字元,主要是對<,>,&等等價替換,使用的指令碼如下:
html安全處理sed指令碼:
#htmlencodes/&/\&/gs/>/\>/gs/</\</g
純文字到html轉換sed指令碼:
#在第一行之前插入CSS1{i\<div class="smstong">\<style>\.smstong{font-size:14px;}\h1,h2,h3{font-family:"黑體";}\h1{font-size:20px;}\h2{font-size:18px;}\h3{font-size:16px;}\ul{list-style-type:none;}\p{font-size:14px; text-indent:2em;}\p.summary{font-size:14px; font-family="楷體";}\</style>}#題目必須是一行/[[:space:]]*標題/d#摘要/[[:space:]]*摘要[::]/{i\<p class="summary">:summaryns;^$;</p>;t doneb summary}#各級標題s/^[0-9]\+[^.]*$/<h1>&<\/h1>/t dones/^[0-9]\+\.[0-9]\+[^.]*$/<h2>&<\/h2>/t dones/^[0-9]\+\.[0-9]\+\.[0-9]\+[^.]*$/<h3>&<\/h3>/t done#原始輸出,帶nochange的都是不變輸出/^[[:space:]]*:nochange/{s//<pre>/:prens;nochange:[[:space:]]*$;</pre>;t doneb pre}#列表開始,以空行結束/^[[:space:]]\+([1a]).*$/{s;;<ul><li>&</li>;:listns;^$;</ul>;t dones;^.*$;<li>&</li>;b list}#段落開始,以空格開始,以空行結束/^[[:space:]]\+\(.*\)$/{s//<p>\1/:paran/^$/s;;</p>;t doneb para}#其他都是原始輸出:done${a\</div>}
附本文的純文字版本:
標題: 非互動式編輯器sed摘要:總結sed的功能與特點、基本文法與使用注意事項。最後給出幾個小例子,特別是用於把本文從純文字轉化為CSDN部落格的指令碼例子。1 sed 概述 sed是Stream EDitor的縮寫。它是一種非互動編輯器,這與互動vim區別非常明顯。1.1 主要使用領域(1) 大量文檔的批處理編輯;(2) 把編輯指令嵌入到指令碼中。1.2sed的獨特之處(1)逐行處理文本,每次只處理一行文本。帶來的好處是無論要處理的檔案有多大,sed只是讀取處理的一行進行處理,佔用記憶體小,速度快;(2)完全支援管道,能從標準輸入讀取,寫入標準輸出,適合unix管線處理;(3)不改變源檔案,所有編輯命令操作的都是模式空間,產生的結果輸出到標準輸出中,無需擔心破壞源檔案。1.3 sed的一般工作流程(1)從輸入讀取第一行到模式空間;(2)判斷當前當前行是否滿足行約束,如果不滿足,則轉到(4);(3)對模式空間進行編輯操作;(4)列印模式空間內容到標準輸出;(5)判斷當前行是否是最後一行,如果是,則轉到(7);(6)清空模式空間,並讀取下一行,轉到(2); ** 保持空間不會清除 **(7)結束.需要注意的是,行約束只是判斷編輯的準則,不滿足行約束的行也會被sed自動讀取到模式空間,因為sed會從頭到尾處理輸入的--所有行--(例外:q命令會導致sed提前退出)。2 sed的行約束sed的基本文法格式非常簡潔,就是:行約束加上編輯命令,如果不指定行約束,則預設所有行都滿足。其中行約束有兩種形式,一是單獨行約束,二是連續塊約束。幾個例子:(1) sed -e 'd' myfile 所有行都滿足行約束(2) sed -e '11d' myfile 只有第11行滿足行約束(3) sed -e '11,23d' myfile 11,12,...,23,這些行滿足行約束(4) sed -e '/^[a-z]/d' myfile 匹配Regex^[a-z]的行滿足行約束(5) sed -e '/regex1/,/regex2/d' myfile 第一個匹配regex1的行滿足,後續行都滿足,直到第一個滿足regex2的行,再往後的行不滿足。判斷流程是:(a)當前行是否滿足regex1,如果不滿足,讀取下一行,轉到(a);(b)進行編輯操作;判斷當前行是否滿足regex2,如果滿足則後續所有行都不再滿足;否則轉到(b)。據此可以推出: 3,1表示從3,...都滿足,因為第三行後面不可能再出現第一行; 3,3表示只有第三行滿足。可見,使用,來表示連續塊。另外,sed支援行約束的嵌套,如下::nochange/regex1/{操作1/regex2/操作2操作3}nochange:此時只用同時匹配regex1和regex2的行才滿足行約束。最後需要說的是,sed還支援取反約束!,如$!表示除了最後一行之外的所有行都滿足。3 sed 基本命令在詳細講解編輯命令之前,需要首先提醒大家,由於sed的多個編輯命令作用於同一個模式空間,所以命令的順序非常重要。相同的一組命令,順序不同,結果可能完全不同。3.1 =、p、dp輸出模式空間內容對應的行號,p則輸出模式空間的內容,d是刪除模式空間內容,執行流結束。3.2 s這可能是最重要的sed編輯命令了,用於替換文本。文法格式為 s/要匹配的正則/新內容/。預設只會替換掉一行中第一個匹配的內容,如果要替換行中所有匹配的地方,則使用s/正則/新內容/g。其實這裡最重要的是Regex的知識,這與grep裡的正則完全相同。需要注意的是,分隔字元/可以是任一字元,再就是"新內容"部分也有自己特定的元字元(如&),這與正則的元字元不同。3.3 i、a、ci用於在當前模式空間的上面插入一行或多行、a類似i只是在模式空間下面插入、c則是替換模式空間的內容。這組命令的特定是需要其文法格式是多行,所以基本都寫在sed指令檔中。如:i\在當前行下面插入的第一行\在當前行下面插入的第二行3.4 n前面我們的討論中,sed的執行流都是先讀取一行到模式空間,然後執行編輯,再輸出, 當前行完全處理後sed首先清空模式空間,然後自動再讀取下一行到模式空間中。其實在執行編輯的過程中也可以通過命令n(ext)來讀取檔案的下一行,此時模式空間裡的內容被下一行完全替換,並且當前的行號增1,但是控制流程沒有回到起始點,而是在n(ext)命令後繼續。例如如下命令會列印檔案的偶數行::nochangesed -n -e 'n;p'nochange:3.5 r、R、w、W這四個命令用於讀寫外部檔案,應用較少,暫且不說。4 sed 進階命令4.1 保持空間與模式空間 N、G、H、g、h、x、D、P模式空間在前面已經說過了,但是前面我們的例子中,模式空間裡都只存在一行文本。其實模式空間不局限於一行,可以有多行。除模式空間外,sed還有一個輔助的記憶體空間,叫做保持空間。有了這兩個記憶體空間,sed可以說是無所不能。前面我們使用過了n命令,作用是讀取下一行到模式空間,替換掉當前模式空間的內容。其大寫字元N則不同,雖都是讀取下一行內容,但是N會把讀取的內容附加到當前模式空間內容的後面,而不是替換。g用於讀取保持空間內容到模式空間,h則相反,大寫版本的命令不是覆蓋而是附加,x則用於交換模式空間和保持空間的內容。d是刪除整個模式空間的內容,而D則是刪除模式空間中的第一行,兩者都會導致執行流重新回到最開始,但是如果模式空間不為空白,sed不會讀取輸入的下一行。 P與p對應,列印的是模式空間的第一行。4.3 執行流 b、t正常情況下,sed的一組命令就是按照書寫的先後順序執行,為了一些進階的編輯,sed提供了改變順序執行流的特殊命令。其文法格式與C語言非常類似。b是branch的縮寫,t是test的縮寫。 b代表無條件轉移,t表示條件轉移,依據的是上次t之後最近一次s命令的替換成功與否。:nochange編輯操作1:mylabel編輯操作2[address]t mylabelnochange:5 注意事項5.1 不同環境的元字元定義sed的行號約束使用$表示最後一行,而正則中$則表示行尾位置,s命令的新內容部分也有&等自己的特殊元字元。如果再加上shell本身的元字元則情況更加複雜,所以一定要注意元字元的使用環境。6 實用的sed指令碼6.1 Unix分行符號Windows分行符號相互轉化:nochangesed -e 's/$/\r/' myunix.txt > mydos.txtsed -e 's/.$//' mydos.txt > myunix.txtnochange:6.2 行序反轉 該功能類似於tac命令sed -e '1!G;h;$!d' forward.txt > backword.txt6.3 把純文字轉化為樣式自訂的部落格文章在CSDN上寫部落格,其內建的編輯器用的很不爽,而且CSDN部落格系統也不支援使用者自訂樣式。這讓使用者情何以堪!我本人喜歡用vim純文字方式寫東西,所以想到了使用sed這個強大的編輯器來把純文字轉為自訂樣式的CSDN部落格。純文字格式的規定是:段落開頭必須空2格,標題定格寫以1,1.1,1.1.1的形式定義,列表以(1),(2),...或(a),(b),...來定義。各結構之間使用空行分隔開。使用:nochange和nochange:包圍的文本原樣輸出,本文自身就是有下面的sed指令碼自動產生的。在進行處理之前,先進行安全html編碼處理,防止原文中有和html衝突的字元,主要是對<,>,&等等價替換,使用的指令碼如下:html安全處理sed指令碼::nochange#htmlencodes/&/\&/gs/>/\>/gs/</\</gnochange:純文字到html轉換sed指令碼::nochange#在第一行之前插入CSS1{i\<div class="smstong">\<style>\.smstong{font-size:14px;}\h1,h2,h3{font-family:"黑體";}\h1{font-size:20px;}\h2{font-size:18px;}\h3{font-size:16px;}\ul{list-style-type:none;}\p{font-size:14px; text-indent:2em;}\p.summary{font-size:14px; font-family="楷體";}\</style>}#題目必須是一行/[[:space:]]*標題/d#摘要/[[:space:]]*摘要[::]/{i\<p class="summary">:summaryns;^$;</p>;t doneb summary}#各級標題s/^[0-9]\+[^.]*$/<h1>&<\/h1>/t dones/^[0-9]\+\.[0-9]\+[^.]*$/<h2>&<\/h2>/t dones/^[0-9]\+\.[0-9]\+\.[0-9]\+[^.]*$/<h3>&<\/h3>/t done#原始輸出,帶nochange的都是不變輸出/^[[:space:]]*:nochange/{s//<pre>/:prens;nochange:[[:space:]]*$;</pre>;t doneb pre}#列表開始,以空行結束/^[[:space:]]\+([1a]).*$/{s;;<ul><li>&</li>;:listns;^$;</ul>;t dones;^.*$;<li>&</li>;b list}#段落開始,以空格開始,以空行結束/^[[:space:]]\+\(.*\)$/{s//<p>\1/:paran/^$/s;;</p>;t doneb para}#其他都是原始輸出:done${a\</div>}nochange: