用sed替換跨行內容

轉載:http://www.fwolf.com/blog/post/346


sed是*nix下方便的行編輯工具,經常用來替換文件的內容,sed一般都是處理單行的,但通過它的一些內建功能,也能實現跨行替換(即要替換的內容有多行內容)。

解決方法主要來自網上搜到的一篇文章,但文中的大俠並沒有解釋得特別清楚,我對照着其他兩個更晦澀的例子(),結合man搞懂了之後,記錄於此。

假設我們的目標文件test內容是這樣的:

file content
  aabbcc<<<comment part 1
  comment part 2>>>
  ddeeff

現在需要把[[[…]]]這一段替換爲“COMMENT”(爲了說明的必要,沒有用容易和正則相混淆的字符比如//*{}[]等來舉例子),那麼sed語法應當是:

:begin
/<<</,/>>>/ {
    />>>/! {
        $! {
            N;
             b begin
        }
    }
    s/<<<.*>>>/COMMENT/;
}

上述語句存儲在test.sed中,那麼執行的方式和結果就是:

$ sed -f test.sed test
        file content
          aabbccCOMMENT
          ddeeff

把正則直接寫到命令裏面也可以,用“;”來分隔命令即可:

$ sed -e ":begin; /<<</,/>>>/ { />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
        file content
          aabbccCOMMENT
          ddeeff

注意右花括號之後也要加上分號“;”,如果再加上-i參數就可以直接把改動寫到原文件中去了。

怎麼樣?看懂了麼?我來詳細說明吧,看那個多行命令的test.sed文件的內容:

  • 首先花括號{}代表命令塊的開始,類似c的語法,後面就不再說了。

  • :begin,這是一個標號,man中叫做label,也就是跳轉標記,供b和t命令用,本例中使用了b命令。

  • /<<</,/>>>/,這是一個地址範圍(Addresses),後面{}中的命令只對地址範圍之間的內容使用。其中逗號前面的部分是開始地址,逗號後面是結束地址,都是正則表達式。由於sed是“流”式“行”處理,所以結束地址是可以省略的,即如果地址的結束範圍不存在,那麼將一直處理到文件結尾。本例中使用這個地址範圍主要是縮小處理的數據量,因爲雖然後面用N命令把對一行的處理擴展爲了多行,但如果從文件開頭一直N擴展到<<<出現爲止,buffer中要處理的字符串可能會很長,影響效率。所以去掉這個處理範圍也是能夠得到正確結果的,比如:

    $ sed -e ":begin; { />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
    or
    $ sed -e "{:begin;  />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
    
  • />>>/!>>>是要替換內容的結束標記,帶上!就是說當一行處理完畢之後,如果沒有發現結束標記。。。

  • $!$在正則中表示字符串結尾,在sed中代表文件的最後一行,本句和上一句結合起來的意思就是:如果在本行沒有發現結束標記,並且當前掃描過的行並不是文件的最後一行。

  • N;,把下一行的內容追加(append)到緩衝區(pattern)之後,在我們的例子中,在處理aabbcc<<<comment part 1這一行的內容時,就會執行到這裏,然後把下一行的內容comment part 2>>>一起放入緩衝區,相當於“合併”成了一行(sed的緩衝區中默認都只會包含一行的內容)。

  • b begin,由於仍然沒有找到結束標記<<<(注意上一條說的緩衝區還沒有被處理),所以在這裏跳回到標號begin,重新開始命令。如果開始和結束標記之間間隔了多行,那麼就會有多次跳轉發生。

  • s/<<<.*>>>/COMMENT/;,終於,/>>>/!不再匹配成功,也就是我們已經找到了結束標記,那麼用s命令來進行替換。如果開始和結束標記在一行的話,就會越過上面那些複雜的處理,直接執行到這裏了。

介紹完畢,收工。

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