sed學習總結及執行個體

來源:互聯網
上載者:User

sed是簡單小巧但功能非常強大的工具。在上一篇部落格中自己註解了sed的info文檔中那些比較複雜的指令碼樣本,最近工作中使用到sed,複習了一下,再做一篇總結放在這裡:

  • sed有兩塊緩衝區:pattern space和hold space。pattern space中存放待處理的目標文本,hold space供程式員自己使用。
  • sed處理流程:
    1. 清空pattern space。(如果這是對該輸入資料流的第一次讀取,則也將hold space初始化為空白)
    2. 從輸入資料流中讀取一行,去除末尾的分行符號,將其放入pattern space。
    3. 執行sed指令碼。sed指令碼由一系列命令組成。因為sed是按行處理,所以sed命令的含義主要是“對於什麼樣的行,則進行什麼樣的處理”。例如,“把第3行刪除”,或者“如果這一行包含this,則把其中的this替換成that”。其中“包含什麼樣的行”由address指定,“執行什麼樣的處理”由sed命令序列表達。
    4. 指令碼執行完畢,如果輸入資料流中還有內容,轉至1,開始下一輪處理。
  • 所以,學習sed,就是學習如何“指定需要處理的行”(address),以及學習我們能使用sed“進行什麼樣的操作”(command)。
  • 注意的是,sed都是按行處理,即使指定的是一個address range,比如sed -e '3,7s/this/that/g',s命令會被執行5次(從第3行到第7行每行執行一次)而不是只執行一次就將第3到第7行的所有this換成that。可以使用sed的N命令或者其他命令讀入多行文本到pattern space。
  • sed使用/M或者/m來切換到"multiple line mode"(即多行處理模式)。所謂“多行處理模式”,是指在這種模式下,元字元^不再表示字串起始位置,而是表示分行符號後面的Null 字元串,元字元$也不再表示字串末尾位置,而是表示分行符號前面的Null 字元串。自我感覺這種模式沒有任何作用,只會把問題弄得混亂,因為完全可以使用\n來匹配分行符號,將其作為一般字元用在Regex中。
  • sed中的調試非常簡便,可以使用上一篇部落格中的那個python指令碼來調試sed,也可以使用sed中的l命令或者p命令來輸出pattern space中的內容進行調試。在以下的“去除多餘空行“的方法二中使用l命令進行調試。

練習幾個例子,以便將來溫習:準備以下檔案:

hbwang@jcwkyl:/home/hbwang$cat testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty
  1. 去掉所有分行符號:

    • 方法一:vim開啟檔案,執行:1,$s/\n//即可。這裡使用sed的方法:讀入一行,刪除分行符號,再讀入下一行,直到整個檔案處理結束。實現如下:
      hbwang@jcwkyl:/home/hbwang$sed -e :a -e "N;s/\n//;ba" testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      命令解釋:-e參數用來添加執行命令。":a"用來設定一個名字為a的label以供後來的goto使用(sed中有兩個跳轉命令:b和t。b為無條件跳轉,t為當上一個s命令替換成功時跳轉)。"N;s/\n//;ba"用分號隔開了三條命令。在執行這些命令之前,pattern space中存放著一個輸入行(其末尾的分行符號已經去掉),N命令在pattern space後面追加一個分行符號並從輸入資料流中讀入下一行。此時pattern space中的內容就是輸入資料流中的連續兩行文本的拷貝,用s命令去掉分行符號,然後再用b命令無條件跳轉到label
      ":a"上繼續處理後面的文本。執行結果如上所示,成功達到了目標。
      這裡有個細節。如果複習一下開頭所說的sed的處理流程(execution cycle),會發現每一輪開始sed都會自己刪除pattern space的內容並從輸入資料流中讀取下一行。照剛才這種方法,pattern space中兩行之間的分行符號去掉了,但第二行末尾的分行符號似乎沒有機會被刪除。然而實驗結果證明這種方法是對的。不妨再驗證一下,在每次替換後用l命令查看一下pattern space中的內容(l命令會以raw format來輸出pattern space中的內容,其中特殊字元都會以其轉義形式輸出,例如分行符號會被輸出成為\n,字串末尾會輸出一個$來標記):

      hbwang@jcwkyl:/home/hbwang$sed -e :a -e "N;s/\n//;l;ba" testabcde f$abcde f1     2 345$abcde f1     2 345en d$abcde f1     2 345en d$abcde f1     2 345en d$abcde f1     2 345en d$abcde f1     2 345en d$abcde f1     2 345en dint main () {$abcde f1     2 345en dint main () {    printf("hello\\n");$abcde f1     2 345en dint main () {    printf("hello\\n");    return \0;$abcde f1     2 345en dint main () {    printf("hello\\n");    return \0;}$abcde f1     2 345en dint main () {    printf("hello\\n");    return \0;}$abcde f1     2 345en dint main () {    printf("hello\\n");    return \0;}$abcde f1     2 345en dint main () {    printf("hello\\n");    return \0;}last empty$abcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      可以看到,pattern space中的內容一直沒有被清除:刪除第一、二行之間的分行符號後,又讀入第三行,刪除第二、三行之間的分行符號,直到整個檔案處理完畢。
      那如果要實現剛才設想的情況,只刪除第奇數個分行符號而保留第偶數個分行符號,只需要讓sed去除一個分行符號後直接開始下一輪執行即可,用d命令可以做到:

      hbwang@jcwkyl:/home/hbwang$sed -e :a -e "N;s/\n//;p;d" testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      d命令會刪除pattern space中的內容,所以在刪除前先用p命令把pattern space中的內容輸出出來。

    • 方法二:讀入整個檔案,用一條命令刪除所有分行符號。
      因為sed在每個cycle開始都會清空pattern space,所以要使用sed讀入整個檔案,要麼在一個cycle中用一個迴圈把所有行都讀入到pattern space,要麼利用hold space不會被清空的特性,每讀一行就把它添加到hold space中,整個檔案讀完,hold space中也就儲存了整個檔案的內容。sed中只支援兩種跳轉方式,即:

      • if(是目標行) { /* handle */ } else { /*非目標行 */}
      • if(替換成功) { goto label; } else { /* 繼續執行 */}

      用第一種跳轉方式讀取整個檔案容易些,實現如下:

      hbwang@jcwkyl:/home/hbwang$sed -e '$!{H;d}' -e '${H;x;s/\n//g;}' testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty或者:hbwang@jcwkyl:/home/hbwang$sed -n -e '$!H' -e '${H;x;s/\n//gp}' testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty或者:hbwang@jcwkyl:/home/hbwang$sed -n -e '$!H' -e '${H;g;s/\n//gp}' testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      其中,"$"是最後一行地址,"$!"表示“不是最後一行”。實現1,'$!{H,d}',表示如果不是最後一行,用H命令在hold space上添加一個分行符號並把pattern space中的內容append到hold space,然後刪除pattern space(如果不刪除,因為我們沒有指定-n選項,所以pattern space的內容會在進入下一輪cycle前輸出出來),'${H;x;s\n//g;}'表示如果是最後一行,用H命令把最後一行添加到hold space,用x命令交換pattern space和hold
      space(其目的是把hold space內容放到pattern space以便s命令處理),然後用s命令刪除所有分行符號。實現二,和實現一相同,不過指定了-n選項,所以需要用p命令輸出自己希望輸出的東西。實現三使用g命令,與x命令不同的是g命令直接用hold space的內容覆蓋pattern space的內容。

  2. 去掉所有空行空行是只包含空白字元的行。這個處理非常簡單:如果當前pattern space中只包含空白字元,刪除它就可以:
    hbwang@jcwkyl:/home/hbwang$sed -e "/^[ \t]*$/d" testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty
  3. 去掉多餘的空行
    • 方法一:將連續多個空行變成一個,如果只有一個空行,則不變。有一個簡單思路:如果當前行是空行,則刪除當前行後續的所有連續空行。換一種描述:對所有的空行,刪除其後續的所有連續空行。第二種描述更容易寫出sed實現,因為sed準系統就是“對什麼樣的行”進行“什麼樣的操作”。寫出來的指令碼如下:

      hbwang@jcwkyl:/home/hbwang$sed -e '/^[ \t]*$/{:a;N;s/^\([ \t]*\n\)\+/\1/;ta}' testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      其中/^[ \t]*$/表示匹配空行,當從輸入資料流中讀取到一個空行時,就執行後面{}括起來的命令序列:繼續從輸入資料流中讀取下一行,如果發現pattern space中是兩個連續空行,則把這連續的兩個空行替換成一個空行。

    • 方法二:像冒泡排序演算法那樣,將相鄰的兩個空行替換成一個空行。實現:
      hbwang@jcwkyl:/home/hbwang$sed -e ":a; N; s/\n\([ \t]*\n\)\1/\n\1/g; ba" testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      如果不清楚這條sed命令是如何完成任務的,可以使用l命令輸出每次執行s命令前的pattern space中的內容,以理解s命令為什麼寫成那樣:

      hbwang@jcwkyl:/home/hbwang$sed -n -e ":a ; N; l 200; ba" testabc\nde f$ <===執行到N時,pattern space中是abd$,執行命令N後,pattern space中是abd\nde f$abc\nde f\n1     2 345$abc\nde f\n1     2 345\nen d$abc\nde f\n1     2 345\nen d\n$abc\nde f\n1     2 345\nen d\n\n$abc\nde f\n1     2 345\nen d\n\n\n$abc\nde f\n1     2 345\nen d\n\n\n\n$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");\n    return 0;$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");\n    return 0;\n}$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");\n    return 0;\n}\n$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");\n    return 0;\n}\n\n$abc\nde f\n1     2 345\nen d\n\n\n\n\nint main () {\n    printf("hello\\n");\n    return 0;\n}\n\n\nlast empty$

      以上,"l 200"表示以raw format輸出pattern space的內容,200指定一行輸出200個字元。

    • 方法三:和方法一類似,比方法一更簡單的實現:
      hbwang@jcwkyl:/home/hbwang$sed -e "/^[[:blank:]]*$/{h; s/.*//; :a; N; s/^\n[[:blank:]]*$//; ta; s/^\n//; H; x;}" testabcde f1     2 345en dint main () {    printf("hello\n");    return 0;}last empty

      這裡用POSIX標準的方括號運算式[:blank:]表示空格和定位字元,和方法一相同,當讀到一個空行時,就執行後面的命令序列:首先把讀取到的空行原樣儲存到hold space中去(h命令的作用),然後s/.*//命令用來清空pattern space中的內容(GNU版本的sed另外提供了z命令來清空pattern spac),N在pattern space中append一個分行符號然後從輸入資料流中讀取下一行append到pattern space,s/^\n[ \t]*$//命令用來刪除空行(因為N命令會在append下一行前先在pattern
      space中append一個分行符號,所以s命令中匹配了分行符號),如果替換成功,說明方才N命令讀取的確實是一個空行,則用t命令跳轉到label a去迴圈執行以刪除空行。如果替換失敗,則表示N命令讀取到了一個非空行,則先去掉pattern space的第一個分行符號(這個分行符號是被N命令自動添加的),然後用H命令將pattern space中的內容append到hold space,最後用x命令把hold space中的內容放到pattern space做為最終的處理結果,這裡的x命令也可以用g命令。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.