隨筆-124  評論-194  文章-0  trackbacks-0
          什么是sed?
          sed 是在 UNIX ® 操作系統上運行的一個非交互式上下文編輯器。sed 被設計在下列三種情況下發揮作用:
          1) 編輯那些對舒適的交互式編輯而言太大的文件。
          2) 在編輯命令太復雜而難于在交互模式下鍵入的時候編輯任何大小的文件。
          3) 要在對輸入的一趟掃描中有效的進行多個‘全局’編輯函數。
          因為每次只把輸入的某些行駐留在內存中,并且不使用臨時文件,所以可編輯的文件的有效大小,只受限于輸入和輸出要同時共存于次級存儲的要求。
          可以單獨的建立復雜的編輯腳本并作為給 sed 的命令文件。對于復雜的編輯,這節省了可觀的鍵入和隨之而來的錯誤。從命令文件運行 sed 高效于作者所知道的任何交互式編輯器,甚至包括能用預先寫好的腳本驅動的編輯器。

          sed是如何操作的?
          sed 缺省的把標準輸入復制到標準輸出,再把每行寫到輸出之前可能在其上進行一個或多個編輯命令。這種行為可以通過命令行上的標志來更改。
          sed編輯命令的一般格式為:
              [address]command [parameter]

          一個或兩個[address]是可以省略的;可以用任何數目的空白或 tab 把地址和函數分隔開。sed函數必須出現。依據給出的是哪個函數,參數可能是必需的或是可選的。忽略在這些行開始處的 tab 字符和空格。

          sed如何對文件的行操作的步驟?
          1)sed對[address]里匹配的行,執行命令并輸出在stdout。
          2)sed在對匹配的行的執行完所有命令后,自動跳到下一個匹配行重復命令執行。
          3)sed對文本行的操作,并不會更改文本的行內容。sed是從文本中調出行內容,并備份,然后在這備份上執行sed命令,最終在stdout上輸出操作后的行。


          sed命令行格式和標志:
          sed命令格式:sed [option] 'sed-script' file
          在命令行上option:
              * -n:告訴 sed 不復制所有的行,只復制 p 函數或在 s 函數后 p 標志所指定的行。
              * -e:告訴 sed 把下一個參數接受為編輯命令。
              * -f:告訴 sed 把下一個參數接受為文件名;這個文件應當包含一行一個的編輯命令。

          什么是模式空間?
          模式匹配的范圍叫做模式空間。一般而言,模式空間是輸入文本中某一行,但是可以通過使用 N 命令把多于一行讀入模式空間

          sed如何從輸入文件里選擇編輯的行?
          編輯命令要應用于其上的,輸入文件中的行可以通過地址來選擇。地址可以是行號或者是上下文地址。

          通過用花括號(‘{ }’)組合(group)命令,可以用一個地址(或地址對)來控制一組命令的應用。

          sed的行號是十進制整數。在從輸入讀入每一行的時候,增加一個行號計數器;行號地址匹配(選擇)導致這個內部計數器等于地址行號的輸入行。計數器在多個輸入文件上累計運行,在打開一個新文件的時候它不被復零(reset)。

          作為特殊情況,字符 $ 匹配輸入文件的最后一行。

          上下文地址是包圍在斜杠中(‘/’)的模式(‘正則表達式’)。sed 識別的正則表達式被構造如下:
             * 1) 普通字符(不是下面討論的某個字符)是一個正則表達式,并且匹配這個字符。
              * 2) 在正則表達式開始處的‘^’符號(circumflex)匹配在行開始處的空(null)字符。
              * 3) 在正則表達式結束處的美元符號‘$’匹配在行結束處的空字符。
              * 4) 字符‘"n’匹配內嵌的換行字符,而不是在模式空間結束處的換行。
              * 5) 點‘.’匹配除了模式空間的終止換行之外的任何字符。
              * 6) 跟隨著星號‘*’的正則表達式,匹配它所跟叢的正則表達式的任何數目(包括 0)的毗連出現。
              * 7) 在方括號‘[ ]’內的字符串,匹配在字符串內的任何字符,而非其他。但是如果這個字符串的第一個字符是‘^’符號,正則表達式匹配除了在這個字符串內的字符和模式空間的終止換行之外的任何字符。
              * 8) 正則表達式的串聯(concatenation)是正則表達式,它匹配這個正則表達式的成員所匹配的字符串的串聯。
              * 9) 在順序的‘"(’和‘")’之間的正則表達式,在效果上等同于沒有它修飾的正則表達式,但它有個副作用,將在下面的 s 命令和緊后面的規定 10 中描述。
              * 10) 表達式‘"d’意味著與在同一個表達式中先前的‘"(’和‘")’中包圍的表達式所匹配的那些字符同樣的字符串。這里的 d 是一個單一的數字;指定的字符串是‘"(’的從左至右的第 d 個出現所起始的字符串。例如,表達式‘^"(.*")"1’匹配開始于同一個字符串的兩次重復出現的行。
              * 11) 孤立的空正則表達式(就是‘//’)等價于編譯的最后一個正則表達式。

          注意:要使用正則表達式的元字符(^ $ . * [ ] " /)中的某一個字符作為文字(去匹配輸入中它們自身的出現),要對這個特殊字符前導一個反斜杠‘"’。

          有的sed命令可能有 0,1 或 2 個地址。在每個命令中都給出了允許的地址的最大數目。地址多于最大允許個數的命令被認為是錯誤的。

          如果命令沒有地址,它應用于輸入中每個行。
          如果命令有一個地址,它應用于匹配這個地址的所有行。
          如果命令有兩個地址,它應用于匹配第一個地址的第一行,和直到(并包括)匹配第二個地址的第一個后續行的所有后續行。接著在后續的行上再次嘗試匹配第一個地址,并重復這個處理。兩個地址用逗號分隔。
          例子:
              /an/         匹配我們樣例文本的第 1, 3, 4 行
              /an.*an/     匹配第 1 行
              /^an/        沒有匹配行
              /./          匹配所有行
              /"./         匹配第 5 行
              /r*an/       匹配第 1,3, 4 行(number = zero!)
              /"(an").*"1/ 匹配第 1 行


          sed的命令格式:
          [address1]command

          [address1],[address2]command

          前者表示sed對匹配地址的行進行操作
          后者表示sed對從匹配地址1的行到匹配地址2的行之間(包括地址1和地址2行)所有的行進行操作

          另外sed命令還可以用大括號進行分組,使其作用于同一個地址:
          [address]{
              command1
              command2
              command3
          }


          [address]{command1;command2;command3}

          注意:sed的[address]是正則表達式,并且要用/ /限定范圍,如:
          /^$/,/^ */d

          如果sed命令之間用;分隔,可以將多個命令寫在同一行,如:n;d;s/sdfd//g

          sed腳本的注釋行第一個字符必須是"#"號,如:
          #wstar.scd:xxxx

          sed的命令函數:

          sed命令集由25個命令組成,而且sed的命令大多是用單個字符表示。

          n(讀取下一行)
          [address]n

          讀取[address]匹配行的下一行
          n命令改變了正常的流控制,導致輸入的下一行取代模式空間中的當前行,如:
          /^".H1/{n;/^$/d}      #將匹配匹配^".H1的下一空行刪除

          =(打印行號)
          [address]=

          在stdout,打印匹配的行號,如:
          /   if/{
          =;p
          }

          p(打印行):
          /address/p

          在stdout,打印匹配的行

          d(刪除):
          [address]d

          如果模行匹配address,那么就刪除整個這一行
          [address1],[address2]d
          刪除匹配address1和address2中間的所有行
          注意:不允許在被刪除的行上做進一步的操作
          例子:
          /^".sp/d
          /^&/,/^".bp/d

          a(追加新行):
          [address]a"
          text

          在匹配address的行后追加新的text行。
          注意:必須是a命令后跟一個"用于轉義第一個行尾,text必須從下一行開始。
          例子:
          /<larry's address>/a"
          4700 Cross Court"
          Freach tjck,IN

          i(插入新行):
          [address]ni"
          text

          表示在匹配address的行前插入新的text行。

          ni"
          text

          表示在第n行插入新的text行

          注意:必須是i命令后跟一個"用于轉義第一個行尾,text必須從下一行開始。
          例子:
          /<larry's address>/i"
          4700 Cross Court

          2i"
          .so macros"
          .ds CH first draft

          q(退出命令):
          [address]q

          退出命令q會使sed停止讀取新的輸入行(并停止將他們發送到輸出)
          /^".".$/q

          nq
          向stdout輸出1到(n-1)行的內容,到第n行時退出sed
          sed '100q' test

          c(行更改):
          [address]c"
          text


          [address1],[address2]c"
          text

          注意:必須是i命令后跟一個"用于轉義第一個行尾,text必須從下一行開始
          c命令刪除當前行并且在該位置放置所提供的文本。當想要匹配行并整個取代它時使用c命令。
          /^".sp/c"
          .sp .5

          /^From/,/^$/{
          s/^From//p
          c"
          <Mail Header Removed>
          }

          y(轉換):
          轉換語法:y/charators/change-charactors/

          y的轉換是根據字符的位置來進行的,//里用的不是正則表達式,只是一般的字符序列。它沒有詞的概念,只是簡單的將對應位置上的字符作替換。
          注意:charactors和change-charactors的字符數目要一致
          y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/

          s(替換):
          替換命令語法為[address]s/pattern/replacement/flags

          修飾替換標志flags是:
          n     1-512之間的一個數字,表示在模式空間里對匹配pattern的字符串第n次出現后開始進行替換,將替換后的行輸出stdout
          g     對模式空間里匹配pattern的字符串進行全局替換,將替換后的行輸出stdout
          p     將匹配行的內容替換后輸出stdout
          w file 將模式空間內容寫到文件file中
          替換命令s應用于與[address]匹配的行。如果沒有指定行,那么就應用于與pattern匹配的所有行。

          在repalcement里可以利用&符號,表示在替換字符串中引用pattern整個匹配內容,如:
          s/See Section [1-9][0-9]*".[1-9][0-9]*/(&)/         #給匹配的字符串添加小括號()

          sed的控制流函數:
          !(非匹配)
          [address]!command   #在不匹配的行,執行sed命令

          [address1],[address2]!command
          非命令導致(寫在同一行上的)下一個命令,應用到所有的且只能是未被地址部分選擇到那些輸入行上。
          /^$/!d
          #刪除不是空行的所有行

          {(命令集)
              組合命令‘{’導致下一組命令作為一個塊而被應用(或不應用)到組合命令的地址所選擇的輸入行上。在組合控制下的的命令中的第一個命令可以出現在與‘{’相同的一行或下一行上。
              組合的命令由自己獨立在一行之上的相匹配的‘}’終止。
              組合可以嵌套。

          :label(標簽)
          標簽是任意不多于7個字符的序列。標簽本身占據一行并以冒號開始:,如:
          :mylabel
          標簽將被分支b和測試t命令調用,改變sed控制流。
          b mylabel
          注意不要在標簽后插入空格

          b(分支)
          branch命令用于腳本將控制權轉移別處。

          [address]b <label>
          label是可選的,如果沒有給出label,sed對該行的控制流就會自動轉移到結尾處。如果有label就繼續執行標簽后的行。
          例子:
          /^".ES/,/^".EE/b
          s/^"/''/
          s/^$/''/
          ...
          s/@DQ@/"/g

          :top
          command1
          commnd2
          /pattern/b top
          command3
          #模式pattern不匹配時,sed執行command1,command2,command3
          #模式pattern匹配時,控制流會跳轉到標簽top處,sed執行command1,command2,command1,command2,command3

          command1
          /pattern/b end
          command2
          :end
          command3
          #模式pattern不匹配時,sed執行command1,command2,command3
          #模式pattern匹配時,sed執行command1,command3

          t(測試是否成功替換)
          test命令用于判斷當前匹配的地址上是否進行了成功替換?成功替換sed的控制流就轉到標簽。

          [address]t <label>
          label是可選的,如果沒有給出label,sed對該行的控制流就會自動轉移到結尾處。如果有label就繼續
          例子:
          /".Rh 0/{
          s/""(.*")"/"2,"3/g
          t break
          s/""(.*")"/"1,"2/g
          t break
          ...
          break:
          more commands
          }
          #若該行有替換,則直接跳到break標簽處執行下邊命令


          匹配多行:

          假設我們的目標文件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命令來進行替換。如果開始和結束標記在一行的話,就會越過上面那些復雜的處理,直接執行到這里了。

          轉自:
          http://www.fwolf.com/blog/post/346
          http://hi.baidu.com/hellolinuxworld/blog/item/5e3aa7080e6350c63bc76309.html


          posted on 2009-09-01 10:12 我愛佳娃 閱讀(3977) 評論(1)  編輯  收藏 所屬分類: 工具使用

          評論:
          # re: SED最佳參考[未登錄] 2009-11-23 20:02 | fly
          找了很久,這篇非常好。感謝分享啊。。  回復  更多評論
            
          主站蜘蛛池模板: 泸西县| 襄垣县| 沙洋县| 高雄县| 竹山县| 乐亭县| 沁源县| 绍兴市| 康保县| 水城县| 西和县| 连城县| 河源市| 隆化县| 海兴县| 都安| 富阳市| 饶河县| 张家港市| 长春市| 平山县| 鄯善县| 禄丰县| 昌邑市| 衢州市| 曲周县| 六盘水市| 武穴市| 介休市| 冕宁县| 尚志市| 韩城市| 余姚市| 射洪县| 濉溪县| 广东省| 潼关县| 喀喇沁旗| 咸丰县| 阆中市| 安顺市|