sed武功心法(info sed翻譯+註解)

来源:http://www.cnblogs.com/f-ck-need-u/archive/2017/09/05/7478188.html
-Advertisement-
Play Games

本文中的提到GNU擴展時,表示該功能是GNU為sed提供的(即GNU版本的sed才有該功能),一般此時都會說明:如果要寫具有可移植性的腳本,應儘量避免在腳本中使用該選項。 本文中的正則表達式幾乎和grep中支持的一樣。但還是有少數幾個是sed自身才能解析的表達式。因此本譯文中只對這些sed自身才支持 ...


  1. 本文中的提到GNU擴展時,表示該功能是GNU為sed提供的(即GNU版本的sed才有該功能),一般此時都會說明:如果要寫具有可移植性的腳本,應儘量避免在腳本中使用該選項。
  2. 本文中的正則表達式幾乎和grep中支持的一樣。但還是有少數幾個是sed自身才能解析的表達式。因此本譯文中只對這些sed自身才支持的正則表達式做翻譯,其餘sed支持的通用性正則表達式見grep命令中文手冊
  3. 此外,除了正則表達式部分,還有些地方沒有進行翻譯,因為個人覺得幾乎用不上,沒必要翻譯。但為了保持文章的完整性,仍給出了原文內容。
  4. 個人建議:如只想瞭解sed的簡單用法,不建議閱讀本譯文;但如果想深入學習或掌握sed,info sed是最佳選擇,很多處理機制在書上(即使是最為流行的《sed & awk》)都沒有深入說明。本文為我第二次翻譯,兩次翻譯過程中收穫都極大。
  5. 譯文中有些地方加上了"(註:)",為本人自行加入,助於理解和說明,非原文內容。PS:直到翻譯完,才發現加了很多很多我個人註釋,儘管是一篇譯文,但也捨不得刪掉這些感想。如果這些"註:"有礙觀感,還望各位能體諒。
  6. 學習sed的過程中,推薦使用"sedsed"調試工具,這對於分析sed處理過程以及pattern space、hold space有很大幫助。但極少數情況下,它的結果可能並不如想象中那樣準確。
  7. 最後,本文是在markdownpad2上寫好才發上來的,由於markdownpad2和博客園markdown編輯器有些語法不同,可能會出現某些符號缺失的問題,雖然檢查了很久,但篇幅太大,難免有遺漏的地方,所以如果發現了哪裡有錯誤或有疑問的地方,盼請指出。同時,也期待與各位交流sed的使用。
  8. 本人譯作集合:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

1 簡介
2 調用方式
3 sed程式
  3.1 sed是如何工作的
  3.2 sed定址:篩選行的方式
  3.3 正則表達式一覽
  3.4 sed常用命令
  3.5 sed的s命令
  3.6 比較少用的sed命令
  3.7 大師級的sed命令(sed標簽功能)
  3.8 GNU sed特有的命令
  3.9 GNU對正則表達式的反斜線擴展
4 一些簡單的示例腳本
5 GNU sed的限制和優點
6 學習sed的其他資源
7 Bugs說明(建議看)

1. Introduction(簡介)

sed是一個流式編輯器。流式編輯器用於對輸入流(文件或管道傳遞的數據)執行基本的文本轉換操作。在某些方面上,sed有點類似於腳本化編輯的編輯器(如ed),但sed只能通過一次輸入流,因此它的效率要更高。但sed區別於其他類型編輯器的地方在於它能篩選過濾管道傳遞的文本數據。
(註:sed只能通過一次輸入流,意味著每次的輸入流只能處理一次,因此像(sed -n '2{p;q}';sed -n 3{p;q}) <filename這樣的命令中,第二個sed語句讀取的輸入流是空流。)

2. Invocation(調用方式)

通常,會使用下麵的方式來調用sed:

sed SCRIPT INPUTFILE...

完整的調用格式為:

sed OPTIONS... [SCRIPT] [INPUTFILE...]

如果不指定INPUTFILE或者指定的INPUTFILE為"-",sed將從標準輸入中讀取輸入流併進行過濾。SCRIPT是第一個非選項參數,sed僅在沒有使用"-e"或"-f script_file"選項時才將其當作是script部分而非輸入文件。

可以使用下麵的命令行選項來調用sed:

'--version'
輸出sed的版本號並退出。

'--help'
輸出sed命令行的簡單幫助信息並退出。

'-n'
'--quiet'
'--silent'
預設情況下,sed將在每輪script迴圈結束(How 'sed' works: Execution Cycle)時自動輸出模式空間中的內容。該選項禁止自動輸出動作,這種情況下,只有顯式通過"p"命令來產生對應的輸出。

'-e SCRIPT'
'--expression=SCRIPT'
向SCRIPT中添加命令集,讓sed根據這些命令集來處理輸入流。(註:其實就是指定pattern和對應的command)

'-f SCRIPT-FILE'
'--file=SCRIPT-FILE'
指定包含command集合的script文件,讓sed根據script文件中的命令集處理輸入流。

'-i[SUFFIX]'
'--in-place[=SUFFIX]'
該選項指定要將sed的輸出結果保存(覆蓋的方式)到當前編輯的文件中。GNU sed是通過創建一個臨時文件並將輸入寫入到該臨時文件,然後重命名為源文件來實現的。

該選項隱含了"-s"選項。

當到達了文件的尾部,臨時文件被重命名為源文件的名稱。如果還提供了SUFFIX,則在重命名臨時文件之前,先使用該SUFFIX先修改源文件名,從而生成一個文件備份。
(註:臨時文件總是會被重命名為源文件名稱,也就是說sed處理完全結束後,仍使用源文件名的文件是sed修改後的文件。文件名中包含了SUFFIX的文件則是最原始文件的備份。例如源文件為a.txt,sed -i'.log' SCRIPT a.txt將生成兩個文件:a.txt和a.txt.log,前者是sed修改後的文件,a.txt.log是源a.txt的備份文件。)

規則如下:如果擴展名不包含符號"*",將SUFFIX被添加到原文件名的後面當作文件尾碼;如果SUFFIX中包含了一個或多個字元"*",則每個"*"都替換為原文件名。這使得你可以為備份文件添加一個首碼,而不是尾碼,甚至可以將此備份文件放在在其他已存在的目錄下。

如果沒有提供SUFFIX,源文件被覆蓋,且不會生成備份文件。

'-l N'
'--line-length=N'
為"l"命令指定預設的換行長度。N=0意味著完全不換行的長行,如果不指定,則70個字元就換行。

'--posix'
GNU 'sed' includes several extensions to POSIX sed. In order to simplify writing portable scripts, this option disables all the extensions that this manual documents, including additional commands. Most of the extensions accept 'sed' programs that are outside the syntax mandated by POSIX, but some of them (such as the behavior of the 'N' command described in *note Reporting Bugs::) actually violate the standard. If you want to disable only the latter kind of extension, you can set the 'POSIXLY_CORRECT' variable to a non-empty value.

'-b'
'--binary'
This option is available on every platform, but is only effective where the operating system makes a distinction between text files and binary files. When such a distinction is made--as is the case for MS-DOS, Windows, Cygwin--text files are composed of lines separated by a carriage return and a line feed character, and 'sed' does not see the ending CR. When this option is specified, 'sed' will open input files in binary mode, thus not requesting this special processing and considering lines to end at a line feed.

'--follow-symlinks'
該選項只在支持符號連接的操作系統上生效,且只有指定了"-i"選項時才生效。指定該選項後,如果sed命令行中指定的輸入文件是一個符號連接,則sed將對該符號鏈接的目標文件進行處理。預設情況下,禁用該選項,因此不會修改鏈接的源文件。

'-r'
'--regexp-extended'
使用擴展正則表達式,而不是使用預設的基礎正則表達式。sed所支持的擴展正則表達式和egrep一樣,使用擴展正則表達式顯得更簡潔,因為有些元字元不用再使用反斜線"\",但這是GNU擴展功能,因此應避免在可移植性腳本中使用。

'-s'
'--separate'
預設情況下,sed會將命令行中指定文件的所有行當作一個長輸入流。此選項為GNU sed擴展功能,指定該選項後,sed將認為命令行中給定的每個文件都是獨立的輸入流。因此此時範圍定址(如/abc/,/def/)無法跨越多個文件,行號也會在處理每個文件時重置,"$"代表的是每個文件的最後一行,"R"命令調用的文件將繞回到每個文件的開頭

'-u'
'--unbuffered'
使用儘量少的空間緩衝輸入和輸出行。(該選項在某些情況下尤為有用,例如輸入流的來源是"tail -f時,指定該選項將可以儘快返回輸出結果。)

'-z'
'--null-data'
'--zero-terminated'
以空串符號"\0"而不是換行符"\n"作為輸入流的行分隔符。

如果未給定"-e"、"-f"、"--expression"或"--file"選項,則命令行中的第一個非選項參數將被認為是SCRIPT被執行。

如果命令行中在上述選項後還加了任意參數,則都將被當作輸入文件。其中"-"表示的是標準輸入流。如果沒有給定任何輸入文件,則預設從標準輸入中讀取輸入里流。

3. 'sed' Programs(sed程式)

sed程式由一個或多個sed命令組成(註:請勿將sed這個工具理解為sed命令,而應該看作是一個包含很多命令的程式,所以後文中"sed命令"表示的是sed中的子命令,而非sed本身這個命令),這些命令通過"-e"、"-f file"傳遞,或者當沒有指定這兩個選項時,通過第一個非選項參數傳遞。這些傳遞的命令集合稱為sed的執行腳本(SCRIPT),命令的執行順序按照命令行中給定的順序依次執行。

在SCRIPT或SCRIPT-FILE中的多個命令可以使用分號";"進行分隔,或者換行書寫。但有些命令,由於它們的語法問題,導致不支持使用分號作為命令分隔符,因此只能換行書寫,除非它們是SCRIPT或SCRIPT-FILE中的最後一個命令。允許命令的前面有空白字元。

每個sed命令由地址或範圍地址,隨後緊跟單字元代表的命令名稱組成,其中地址部分可以省略。

3.1 How 'sed' Works(sed是如何工作的)

sed維護兩個數據緩衝空間:一直處於活動狀態的模式空間(pattern space)和輔助性的保持空間(hold space)。這兩個空間初始時都為空。

sed通過執行下麵的迴圈來操作輸入流中的每一行: 首先,sed讀取輸入流中的一行,移除該行的尾隨換行符,並將其放入到pattern space中。然後對pattern space中的內容執行SCRIPT中的sed命令,每個sed命令都可以關聯一個地址:地址是一種條件判斷代碼,只有符合條件的行才會執行相應的命令。當執行到SCRIPT的尾部時,除非指定了"-n"選項,否則pattern space中的內容將寫到標準輸出流中,並添加回尾隨的換行符。然後進入下一個迴圈開始處理輸入流中的下一行。

除非指定了特殊的命令(例如"-D"),否則pattern space中的內容在SCRIPT迴圈結束後會被刪除。但是hold space會保留其中的內容(參見sed命令'h', 'H', 'x', 'g', 'G'以獲知兩個buffer空間是如何互相移動數據的)。

(

註:也就是說,sed程式工作時包含內外兩個迴圈:內迴圈是SCRIPT迴圈,迴圈的過程是對模式空間中的內容執行SCRIPT中的命令集合,還包括一個隱含的自動輸出動作用於輸出模式空間的內容到標準輸出中;外迴圈是sed迴圈,讀取下一行到模式空間中,並執行SCRIPT迴圈,SCRIPT迴圈結束後再讀取下一行到模式空間中。使用編程結構描述,結構如下:

for ((line=1;line<=last_line_num;++line))
do
    read $line to pattern_space;
    while pattern_space is not null
    do
        execute cmd1 in SCRIPT;
        execute cmd2 in SCRIPT;
        ……
        auto_print;
        remove_pattern_space;
    done
done

一般情況下,SCRIPT迴圈都只執行一輪就退出併進入外層sed迴圈,因為執行完一次SCRIPT迴圈, pattern space就清空了,但有些特殊命令(如"D"命令)會進入多行模式,使得SCRIPT迴圈結束 時將數據鎖在pattern space中不輸出也不清空,甚至在SCRIPT迴圈還沒結束時就強行進入下一 輪SCRIPT迴圈,在《sed & awk》一書中,這種行為被稱之為"回到SCRIPT的頂端"。其實就相當 於在上面的while迴圈結構中加上了"continue"關鍵字。此外還有命令(如"d")可以直接退出 SCRIPT迴圈進入下一個sed迴圈,就像是在while迴圈中加上了"break"一樣。甚至還有直接退出 sed迴圈的命令(只有2個這樣的命令:"q"和"Q"),就像是加上了"exit"一樣。

auto_printremove_pattern_space動作是隱含動作,分別稱之為自動輸出和清空pattern space。"-n"選項禁用的自動輸出不是禁止隱含動作auto_print,而是讓其輸出空內容(它們是有區別的),並執行remove_pattern_space。只要沒有指定"-n"選項,在SCRIPT迴圈結束時一定輸出pattern space中的全部內容並清空pattern space,只不過某些特殊的命令可以將pattern space中的內容鎖住使auto_print輸出空內容,也使得remove_pattern_space移除不了pattern space的內容。
之所以要特地指出"輸出空內容"並標明它和禁止輸出動作,是因為某些命令(如"a"、"i"、"c"命令)依賴於輸出流,沒有輸出動作就沒有輸出流,這些命令就無法正確完成。

這幾個迴圈、幾個動作的細節非常重要,雖不影響後文單個選項或命令的理解,但如果將命令結合,有時候的結果很可能會出人意料,而sed難就難在命令的合理組合。例如下麵的命令,"a"命令本來是要將xyz插入到aaa所在行後面的,但結果卻插在了aaa行的前面。

echo -e "aaa\nbbb" | sed '/aaa/{a\
> xyz
> ;N}'
xyz
aaa
bbb

)

3.2 Selecting lines with 'sed'(sed定址:篩選行的方式)

(註:sed SCRIPT中的命令由單字元代表的命令和地址組成,地址的作用是匹配當前正在處理的行,如果能匹配成功,就執行與該地址相關聯的命令。地址由定址表達式決定,定址的結果可能是單行,可能是一個範圍,省略定址表達式時表示所有行。)

可以使用下麵任意一種方式進行定址:

'NUMBER'
指定一個行號,sed將僅只匹配該行。(需要註意,除非使用了"-s"或"-i"選項,sed將對所有輸入文件的行連續計數。)

'FIRST~STEP'
這是GNU sed的功能,FIRST和STEP都是非負數。該定址表達式表示從第FIRST行開始,每隔STEP行就再取一次。也就是取行號滿足“FIRST+(N*STEP)” (其中N>=0)的行。因此,要選擇所有的奇數行,使用“1~2”;要從第2行開始每隔3行取一次,使用“2~3”;要從第10行開始每隔5行取一次,使用“10~5”;而“50~0”則表示只取第50行。

'$'
該符號匹配的是最後一個文件的最後一行,如果指定了"-i"或"-s",則匹配的是每個文件的最後一行。
(註:總之,"$"匹配的是每個輸入流的最後一行)

'/REGEXP/'
該定址表達式將選擇那些能被正則表達式REGEXP匹配的所有行。如果REGEXP中自身包含了字元"/",則必須使用反斜線進行轉義,即"\/"

空的正則表達式"//"表示引用最後一個正則表達式匹配的結果(命令"s"中的正則匹配部分如果是空正則"//",則一樣如此處理)。註意,正則表達式的修飾符(即下文中的"I"和"M",以及s命令中的修飾符"i"、"I"和"M")是在正則表達式編譯完成之後(註:進行數據匹配時)才生效的,因此,這些修飾符和空正則一起使用時無效(註:會報錯,提示"cannot specify modifiers on empty regexp")。

(註:這裡的修飾符特指定址時可用的修飾符,即I和M。命令s也有修飾符"i"、"I"和"M"。
如:sed '/hold/Is//gogogo/g'能成功,因為第一個定址正則表達式的修飾符"I"的對象是非空集"hold",第二個正則模式"//"沒有指定"i"、"I"或"M"修飾符,所以成功。但sed '/hold/Is//gogogo/gi'會報錯,因為在正則編譯結束後還未開始進行匹配的時候,第二個正則表達式中的修飾符"i"的對象是空集。sed '/hold/I{//Mp}'sed '/hold/I{//Ip}'也都是失敗的,原因都是第二個正則的修飾符對象是空集。

'\%REGEXP%'
('%'可以使用其他任意單個字元替換。)

這和上一個定址表達式的作用是一樣的,只不過是使用符號"%"替換了符號"/"。當REGEXP中包含"/"符號時,使用該定址表達式就無需對"/"使用反斜線"\"轉義。但如果此時REGEXP中包含了"%"符號時,該符號需要使用"\"轉義。
總之,定址表達式中使用的分隔符在REGEXP中出現時都需要使用反斜線轉義。

'/REGEXP/I'
'\%REGEXP%I'
正則表達式的修飾符"I"是GNU的擴展功能,表示REGEXP在匹配時不區分大小寫。

'/REGEXP/M'
'\%REGEXP%M'
正則表達式的修飾符"M"是GNU的擴展功能,這可以讓sed直接匹配多行模式下(multi-line mode)的位置。該修飾符使得正則表達式的元字元"^"和"$"匹配分別匹配每個新行後的空字元和新行前的空字元。還有另外兩個特殊的字元序列(\`和\',分別是反斜線加反引號,反斜線加單引號),它們總是匹配buffer空間中的起始和結束位置。此外,元字元點"."不能匹配多行模式下的換行符。
(註:在單行模式下,使用M和不使用M是沒有區別的,但在多行模式下各符號匹配的位置將和通常的正則表達式元字元匹配的內容不同,各符號的匹配位置如下圖所示)

如果沒有給定地址,則會匹配輸入流中的所有行,如果給定了單地址的定址表達式,則這些行會在模式空間中被匹配條件進行匹配。

可以使用逗號分隔的兩個地址表達式(ADDR1,ADDR2)來描述範圍地址。範圍地址表示從符合第一個地址條件的行開始匹配,直到符合第二個地址的行結束。

(註:需要註意,無論行是否符合地址匹配條件,它們都會被讀入pattern space,只不過讀入的行如果不符合地址匹配條件,將直接執行SCRIPT迴圈中的隱含動作,並結束SCRIPT迴圈繼續讀取輸入流的下一行)

如果第二個定址表達式是正則表達式REGEXP,將從符合第一個定址表達式的行開始逐行檢查,以匹配範圍定址的結束行:範圍定址的範圍總是會跨越至少兩行(當然,如果輸入流結束的情形除外)。

如果第二個定址表達式是一個小於或等於第一個表達式代表的行號值,則只會匹配第一個表達式搜索到的那一行。
(註:即如果範圍地址的結束行在起始行的前面,則只會匹配起始行。)

GNU sed還支持以下幾種特殊的兩地址格式,這些都是GNU的擴展功能:

'0,/REGEXP/'

使用行號0作為起始地址也是支持的,就像此處的"0,/REGEXP/",這時sed會嘗試對第一行就匹配REGEXP。換句話說,"0,/REGEXP/"和"1,/REGEXP/"基本是相同的。但以行號0作為起始行時,如果第一行就能被ADDR2匹配,範圍搜索立即就結束,因為第二個地址已經搜索到了;如果以行號1作為起始行,會從第二行開始匹配ADDR2,直到匹配成功。

註意,0地址只有在這一種情況下才是有意義的,實際上並沒有第0行,在其他任何時候使用行號0都會給出錯誤提示。

'ADDR1,+N'
匹配ADDR1和其後的N行。

'ADDR1,~N'
匹配ADDR1和其後的行直到出現N的倍數行,倍數可為隨意整數倍。 (註:可以是任意倍,只要N的倍數是最接近且大於ADDR1的即可。如ADDR1=1,N=3匹配1到3行,ADDR1=5,N=4匹配5-8行。而"1,+3"匹配的是第一行和其後的3行即1-4行。)

在地址的後面追加"!"符號指定反轉匹配的含義。意思是,如果"!"跟在範圍定址的後面,則那些匹配的行將不被選擇,而是不匹配的行被選擇。同樣可以適用於單個定址甚至於有悖常理的空定址。

(註:例如,sed -n '3!p' INPUTFILEsed -n '3,5!p' INPUTFILE,甚至是sed -n '!p' INPUTFILE)

(註:

  • sed採用計數器計算,每讀取一行,計數器加1,直到最後一行。因此在讀到最後一行前,sed是不知道這次輸入流中總共有多上行,也不知道最後一行是第幾行。"$"符號表示最後一行,它只是一個特殊的標識符號。當sed讀取到輸入流的尾部時,sed就會為該行打上該標記。無法使用"$"參與數學運算,例如無法使用$-1表示倒數第二行,因為sed在讀到最後一行前,它並不知道這是倒數第二行,此時也還沒打"$"標記,因此$-1是錯誤的定址表達式。另一方面,在本譯文的最開始就說明瞭,sed只能通過一次輸入流,這意味著已經讀取過的行無法再次被讀取,所以sed不提供往回取數據的定址表達式,上面的幾個定址表達式也確實證明瞭這一點。事實上,sed中根本就無法使用"-"做減法或取負數,因為語法不支持。

  • 範圍匹配的是pattern space中的內容,對於regexp1,regexp2這樣的範圍定址自然容易理解,但如果是num1,num2這樣的範圍定址,它可能和想象中的匹配方式不一樣。每讀取一行,sed內部的行號計數器都會"+1",所以行號值總是記錄在記憶體中。當進行行號匹配時,其本質是和記憶體中當前計數器的值進行匹配,如果當前計數器的值在範圍內,則能被匹配,否則無法匹配。因此,從hold space回到pattern space的行或者被修改的行甚至是被刪除過的行,不管這一行的內容在pattern space中是否存在,只要當前計數器值在範圍內,就能匹配。例如多行模式:

echo -e "abc\nfgh" | sed -n 'N;1p'

該命令不輸出任何內容。雖然N讀取下一行後,pattern space兩行中的第一行一直原封不動的放在那,沒做任何處理,但"1p"根本就無法匹配這一行,因為當前計數器的值為2。

)。

3.3 Overview of Regular Expression Syntax(正則表達式一覽)

(註:sed解析、編譯和匹配正則表達式的引擎和grep的引擎一樣,因此本文基本不做翻譯,可以參考我對info grep的譯文:grep命令中文手冊。)

To know how to use 'sed', people should understand regular expressions ("regexp" for short). A regular expression is a pattern that is matched against a subject string from left to right. Most characters are "ordinary": they stand for themselves in a pattern, and match the corresponding characters in the subject. As a trivial example, the pattern

The quick brown fox

matches a portion of a subject string that is identical to itself. The power of regular expressions comes from the ability to include alternatives and repetitions in the pattern. These are encoded in the pattern by the use of "special characters", which do not stand for themselves but instead are interpreted in some special way. Here is a brief description of regular expression syntax as used in 'sed'.

'CHAR'
A single ordinary character matches itself.

'*'
Matches a sequence of zero or more instances of matches for the preceding regular expression, which must be an ordinary character, a special character preceded by '\', a '.', a grouped regexp (see below), or a bracket expression. As a GNU extension, a postfixed regular expression can also be followed by '*'; for example, 'a**' is equivalent to 'a*'. POSIX 1003.1-2001 says that '*' stands for itself when it appears at the start of a regular expression or subexpression, but many nonGNU implementations do not support this and portable scripts should instead use '\*' in these contexts.

'\+'
As '*', but matches one or more. It is a GNU extension.

'\?'
As '*', but only matches zero or one. It is a GNU extension.

'\{I\}'
As '*', but matches exactly I sequences (I is a decimal integer; for portability, keep it between 0 and 255 inclusive).

'\{I,J\}'
Matches between I and J, inclusive, sequences.

'\{I,\}'
Matches more than or equal to I sequences.

'\(REGEXP\)'
Groups the inner REGEXP as a whole, this is used to:

  • Apply postfix operators, like '\(abcd\)*': this will search for zero or more whole sequences of 'abcd', while 'abcd*' would search for 'abc' followed by zero or more occurrences of 'd'. Note that support for '\(abcd\)*' is required by POSIX 1003.1-2001, but many non-GNU implementations do not support it and hence it is not universally portable.

  • Use back references (see below).

'.'
Matches any character, including newline.

'^'
Matches the null string at beginning of the pattern space, i.e. what appears after the circumflex must appear at the beginning of the pattern space.

In most scripts, pattern space is initialized to the content of each line (*note How 'sed' works: Execution Cycle.). So, it is a useful simplification to think of '^#include' as matching only lines where '#include' is the first thing on line--if there are spaces before, for example, the match fails. This simplification is valid as long as the original content of pattern space is not modified, for example with an 's' command.

'^' acts as a special character only at the beginning of the regular expression or subexpression (that is, after '(' or '|'). Portable scripts should avoid '^' at the beginning of a subexpression, though, as POSIX allows implementations that treat '^' as an ordinary character in that context.

'$'
It is the same as '^', but refers to end of pattern space. '$' also acts as a special character only at the end of the regular expression or subexpression (that is, before ')' or '|'), and its use at the end of a subexpression is not portable.

'[LIST]'
'[^LIST]'
Matches any single character in LIST: for example, '[aeiou]' matches all vowels. A list may include sequences like 'CHAR1-CHAR2', which matches any character between (inclusive) CHAR1 and CHAR2.

A leading '^' reverses the meaning of LIST, so that it matches any single character not in LIST. To include ']' in the list, make it the first character (after the '^' if needed), to include '-' in the list, make it the first or last; to include '^' put it after the first character.

The characters '$', '*', '.', '[', and '\' are normally not special within LIST. For example, '[\*]' matches either '\' or '*', because the '\' is not special here. However, strings like '[.ch.]', '[=a=]', and '[:space:]' are special within LIST and represent collating symbols, equivalence classes, and character classes, respectively, and '[' is therefore special within LIST when it is followed by '.', '=', or ':'. Also, when not in 'POSIXLY_CORRECT' mode, special escapes like '\n' and '\t' are recognized within LIST. *Note Escapes::.

'REGEXP1\|REGEXP2'
Matches either REGEXP1 or REGEXP2. Use parentheses to use complex alternative regular expressions. The matching process tries each alternative in turn, from left to right, and the first one that succeeds is used. It is a GNU extension.

'REGEXP1REGEXP2'
Matches the concatenation of REGEXP1 and REGEXP2. Concatenation binds more tightly than '|', '^', and '$', but less tightly than the other regular expression operators.

'\DIGIT'
Matches the DIGIT-th '(...)' parenthesized subexpression in the regular expression. This is called a "back reference". Subexpressions are implicity numbered by counting occurrences of '(' left-to-right.

'\n'
匹配換行符。

'\CHAR'
Matches CHAR, where CHAR is one of '$', '*', '.', '[', '\', or '^'. Note that the only C-like backslash sequences that you can portably assume to be interpreted are '\n' and '\\'; in particular '\t' is not portable, and matches a 't' under most implementations of 'sed', rather than a tab character.

註意,sed支持的正則表達式是貪婪匹配的。例如,從行左向行右匹配時,如果滿足匹配的匹配字元有多個,則會取最長的。(註:例如字元串'abbbc',給定正則表達式"ab*",滿足該正則的字元串序列有"a","ab","abb"以及"abbb",由於是貪婪匹配,它會取最長的,即"abbb")

示例:

'abcdef'
匹配字元串"abcdef"。

'a*b'
匹配0或多個a,但後面需要跟上字元b,例如'b'或'aaaaab'。

'a\?b'
匹配"b"或"ab"。

'a\+b\+'
匹配一個或多個a,且後面需要有一個或多個b,所以"ab"是最短的匹配序列,其他的如'aaaab'、'abbbbb'或'aaaaaabbbbbbb'都能被匹配。

'.*'
'.\+'
都匹配所有字元;但是,第一個匹配任何字串(包含空字串),而第二個只匹配包含至少一個字元的字串(空格也是字元)。
(註:即第一種會匹配所有,包括null字元,第二種不能匹配null字串。例如:sed -n '/.*/p'會匹配空行。sed -n '/.\+/p'不會匹配空行,這是匹配非空行的一種簡便方法。)

'^main.*(.*)'
匹配以"main"開頭的行,且該行還包括括弧。

'^#'
匹配以"#"開頭的行。

'\\$'
匹配以反斜線結尾的行。

'\$'
匹配包含美元符號的行。

'[a-zA-Z0-9]'
在C字元集環境下,這匹配任意ASCII的字母和數字。

'[^ tab]\+'
(此處tab代表一個製表符)匹配不包含空格和製表符的行。通常用於匹配整個單詞。

'^\(.*\)\n\1$'
匹配相鄰兩行完全相同的情況。

'.\{9\}A$'
匹配9個任意字元,後面還帶一個字母A。

'^.\{15\}A'
匹配以任意15個字元開頭但第16個字元是A的行。

3.4 Often-Used Commands(sed常用命令)

如果要掌握sed,下麵幾個命令幾乎是必須掌握的。

'#'
不能跟在定址表達式後。

以"#"開頭的行表示註釋行。

如果要考慮可移植性,需要註意有些sed可能僅支持一條單行註釋,且此時"#"必須是SCRIPT中的第一個字元。

註意:如果SCRIPT中的前兩個字元是"#n",則表示強制開啟選項"-n",而非註釋。如果想要開啟一個註釋行來註釋以字母"n"開頭的字元串,那麼需要使用大寫字母N代替,或者"#"和"n"之間加上一個或多個空白字元。

'q [EXIT-CODE]'
該命令只能在單定址表達式下使用。

表示立即退出sed,而不再處理後續的命令和輸入流。註意,退出sed前,當前pattern space中的內容會被輸出,除非使用了"-n"選項。返回一個退出狀態碼是GNU sed的擴展功能。(註:後文還有一個"Q"命令,和"q"命令作用相同,但退出sed前不輸出當前pattern space中的內容。)

'd'
刪除模式空間中的內容,並立即進入下一個sed迴圈(註:即以"break"的方式直接退出當前SCRIPT迴圈,並讀取輸入流中的下一行,再執行SCRIPT迴圈)。

'p'
輸出當前模式空間的內容(到標準輸出中)。該命令一般只和"-n"選項一起使用。

'n'
輸出模式空間的內容(除非自動輸出功能被禁止),然後讀取下一行到模式空間中替換其中的內容。如果輸入流中沒有下一行供"n"讀取(註:即此前已經讀取了輸入流的最後一行),sed將直接退出,不會執行SCRIPT中後續的命令。

(

註:以下麵兩個命令為例:其中"i"命令為插入字元串並輸出。

[root@xuexi ~]# echo -e "line1\nline2\nline3\nline4" | sed -n 'n;p;i aaa'
line2
aaa
line4
aaa
[root@xuexi ~]# echo -e "line1\nline2\nline3" | sed -n 'n;p;i aaa'
line2
aaa

第二個命令讀取了第三行到pattern space後執行n命令,但此時輸入流中已經沒有下一行供讀取,於是直接結束,鏈後續的"p"命令都沒有執行。

)

'{ COMMANDS }'
使用大括弧將一系列命令組合成一個命令組。在某些時候特別有用,例如相對某一匹配行或某一範圍中的行做多次處理時。

3.5 The 's' Command(sed的s命令)

sed的"s"命令(該命令用於字元串替換)的語法格式為"s/REGEXP/REPLACEMENT/FLAGS"。"s"命令中的字元"/"可以統一被替換成任意其他單個字元(註:在定址正則表達式中斜杠也可以被替換成其他字元,但需要在第一個被替換字元前加上反斜線轉義,例如\%REGEXP%,而s命令中替換時無需加反斜線)。如果斜線字元"/"(或被替換後的其他字元)需要出現在REGEXP或REPLACEMENT中,必須使用反斜線進行轉義。

sed的"s"命令算的上是sed程式中最重要的命令,它有很多不同的選項。但它的基本概念很簡單:"s"命令使用REGEXP匹配pattern space中的內容,如果匹配成功,則匹配成功的那部分字元串被替換為REPLACEMENT。

REPLACEMENT中可以使用"\N"(N是從1到9的整數)進行後向引用,所代表的是REGEXP第N個括弧\(...\)中匹配的內容。另外,REPLACEMENT中可以包含未轉義的"&"符號,這表示引用pattern space中被匹配的整個內容(註:是pattern space中的所有匹配,不僅僅只是括弧的分組匹配)。最後,GNU sed為REPLACEMENT還提供了一些"反斜線加單字母"的特殊字元序列,如下:

'\L'
將REPLACEMENTE轉換成小寫,直到遇到了"\U"或"\E"。

'\l'
將REPLACEMENTE中下一個字元轉換成小寫。

'\U'
將REPLACEMENTE轉換成大寫,直到遇到了"\L"或"\E"。

'\u'
將REPLACEMENT中下一個字元轉換成大寫。

'\E'
停止"\L"和"\E"開啟的大小寫轉換。

(

註:使用方法如下示例

shell> echo hello | sed 's/e/ \Uyour\Lname /'
h YOURname llo

)

當使用了"g"修飾符時,大小寫轉換不會從一個正則匹配事件擴展到另一個正則匹配事件。例如,如果pattern space中的內容為"a-b-",執行下麵的命令:

 s/\(b\?\)-/x\u\1/g

得到的輸出為"axxB"。當替換第一個"-"時,"\u"隻影響"\1"代表的空值。當替換"b-"時,由於"\1"代表的是"b-",所以第一個字元"b"會被替換為"B",但添加到pattern space中的"x"仍然為小寫。

另一方面,"\l""\u"會影響REPLACEMENT中的空引用的後一個字元。例如:模式空間內容為"a-b-",執行下麵的命令:

 s/\(b\?\)-/\u\1x/g

將使用"X"(大寫)替換"-",使用"Bx"替換"b-"。如果這樣的結果不是你想要的結果,可以在此例中的"\1"後加上"\E"防止"x"被轉換。

如果想要在最後的REPLACEMENT中包含字面符號"\""&"或換行符,需要在REPLACEMENT中這些字元之前加上反斜線。

sed的"s"命令可以接0或多個如下列出的修飾符(FLAGS):

'g'
使用REPLACEMENT替換所有被REGEXP匹配的內容,而非第一次被匹配到的。

(註:sed任何動作都是在pattern space進行的,字元串替換也如此。不加"g"修飾符時,將只pattern space中第一個被匹配到的內容,加上"g",將替換pattern space中所有被匹配到的內容,因此如果是多行工作模式,第二行或更多行只要能被匹配都會被替換。)

'NUMBER'
為一個整數值N。表示只替換第N個被匹配到的內容。

註意:POSIX標準中沒有說明既指定"g"又指定"NUMBER"修飾符時會如何處理,並且當前各種sed的實現也沒有標準說法。對於GNU sed而言,定義如下:忽略第NUMBER個匹配前的所有匹配,然後從第NUMBER個匹配開始向後重新匹配並替換所有匹配成功的內容。

'p'
替換動作完成後列印新的模式空間中的內容。

註意:當既指定"p"又指定"e"命令時,它們的前後順序不同,會得到兩種不同結果。一般來說,"ep"這種順序得到的結果可能是所期望的結果,但另一種順序"pe"對調試很有用。出於這個原因,當前版本的GNU sed特地解釋了"p"命令在"e"命令前(或後)時,將在"e"命令生效前(或後)輸出內容,因為"s"命令的每個修飾符一般都只展示一次結果。雖然這種行為已經寫入文檔,但未來可能會改變。

'w FILE-NAME'
該子命令表示將模式空間的內容寫入到指定的文件FILE-NAME中。GNU sed支持兩個特殊的FILE-NAME:"/dev/stderr""/dev/stdout",分別表示寫入到標準錯誤和標準輸出中。

'e'
該命令允許通過管道將shell命令的執行結果直接傳遞到pattern space中。當替換動作完成後,將搜索pattern space,發現的命令會被執行,並且執行結果覆蓋到當前pattern space中。它會禁用尾隨換行符,並且如果待執行命令中包含了NULL字元,該命令將不被定義為命令,即表示它是普通字元而不是命令所以不執行。這是GNU sed的功能。

(

註:"s"命令的"e"修飾符似乎只有搜索pattern space並找出其中的命令並執行的功能,沒有選項描述中第一句話所述的功能。但"e"命令有該功能,例如:

echo -e "good\nbad" | sed 'e echo haha'
haha
good
haha
bad

不討論e命令的用法,關於"s"命令的"e"修飾符的用法如下:

文件ttt.txt的內容如下:

[root@xuexi tmp]# cat ttt.txt 
ls /tmp
haha
excute a command

將第二行的"haha"替換成一個命令後使用"e"修飾符,那麼在替換完成後會查找模式空間,如果找到了可以執行的命令就執行。

[root@xuexi tmp]# sed 's/haha/du -sh \/tmp/ge' ttt.txt 
ls /tmp        # 註意這一行沒有執行
18M     /tmp   # 說明執行了du -sh /tmp的命令
excute a command

註意到第一行雖然也是可以執行的命令,但是卻沒有執行,因為"e"是"s"命令的修飾符,需要成功匹配"haha"的行才能符合"e"修飾符。
註意模式空間中的內容是一行一行的,命令將把整行內容作為命令行。例如,如果只將excute替換成du -sh /tmp,那麼模式空間中的內容將是"du -sh /tmp a command",它會對/tmp和當前目錄下的"a和"command"目錄進行"du -sh"統計,但很可能"a"或"command"目錄根本不存在,這時就會報錯。

[root@xuexi tmp]# sed 's%excute%du -sh /tmp%ge' ttt.txt
ls /tmp
haha
du: cannot access 'command': No such file or directory  # 不存在command目錄所以錯誤
18M      /tmp
4.0K     a      # 當前目錄下正好存在a目錄,所以統計了信息

並且如果替換後找到的命令不在行首(有前導空白沒有影響),將出現錯誤,因為是將整行作為命令來執行的。

[root@xuexi tmp]# sed 's%command%du -sh /tmp%ge' ttt.txt         
ls /tmp
haha
sh: excute: command not found

所以更保險的方法是對整行進行替換。

[root@xuexi tmp]# sed 's%^excute.*%du -sh /tmp%ge' ttt.txt 
ls /tmp
haha
18M     /tmp

當然,如果想要執行第一行的"ls /tmp"命令也很簡單,只需匹配這一行。也可以在此命令上進行命令擴展。如下麵將/tmp替換後,模式空間的內容是"ls -ld /tmp;du -sh /tmp",所以會執行這兩條命令。

[root@xuexi tmp]# sed 's!/tmp!-ld /tmp;du -sh /tmp!ge' ttt.txt       
dr-xr-xr-x. 17 root root 8736768 Oct 25 14:11 /tmp
18M     /tmp
haha
excute a command

)

'I'
'i'
這兩個修飾符作用相同,表示在REGEXP匹配的時候忽略大小寫。這是GNU擴展功能。

'M'
'm'
和前文定址表達式中所述的M修飾符作用一致。見M修飾符

(註:除了上述明確的修飾符外,還有一個特殊的不算修飾符的符號"\",當它寫在"s"命令的REPLACEMENT的每個行尾時,表示新轉義當前命令行的行尾,也就是開啟一個新行。例如:

echo 'abcdef' | sed 's/^.*$/\
&\
/'

這表示在abcdef這一行內容前後分別加一個空行,也就是將abcdef這一行嵌入到空行中間。所以可將其理解為換行符"\n",但必須註意,如果轉義新行後有內容,則此新行必須再次使用反斜線終止該行,因為它的行尾已經被轉義了。例如:

echo 'abcdef' | sed 's/^.*$/\
&\
xyz\
/'

echo 'abcdef' | sed 's/^.*$/&\
/'

其實個人覺得使用"\n"方便多了,即容易理解又容易控制。例如上面最後一個命令改為:echo 'abcdef' | sed 's/^.*$/&\n/'。在後文的好幾個例子中都是用了轉義行尾的技巧,所以此處提上一提。

)

3.6 Less Frequently-Used Commands(比較少用的sed命令)

雖然可能用的不如前面所述的命令頻繁,但這些小命令有時候非常有用。

'y/SOURCE-CHARS/DEST-CHARS/'
(y命令中的斜線"/"可以統一被替換成其他單個字元。)

轉換pattern space中能被SOURCE-CHARS匹配的字元為DEST-CHARS,且是一一對應地轉換。

斜線"/"(或被替換為的其他字元)、反斜線"\"和換行符可以出現在SOURCE-CHARS或DEST-CHARS中,但需要使用反斜線進行轉義。(轉義後的)SOURCE-CHARS和DEST-CHARS中字元的數量必須完全相同。

'a\'
'TEXT'
是GNU sed的擴展功能,該命令可以在兩個地址格式的定址表達式後使用。

隊列化該命令後的文本內容(最後一個反斜線"\"是文本結束符,在輸出時該符號會被移除),併在當前SCRIPT迴圈結束時輸出,或從輸入流中讀取下一行時輸出。(註:本質是:只要"有讀取下一行"的動作就會觸發隊列化內容的輸出,不止是"a"、還有"i"和"c"以及"r"等,另外,除了sed迴圈的第一個動作可以讀取下一行,命令"N"和"n"都會讀取下一行,因此它們也會觸發這些隊列化內容的輸出)

輸入的文本內容中如果要出現反斜線字元,需要進行轉義,即"\\"

作為一個GNU擴展功能,如果命令"a"和換行符之間存在非空白字元"\"序列,則此反斜線會開啟新行,並且反斜線後的第一個非空白字元作為下一行的第一個字元。這同樣適用於下麵的"i"和"c"命令。

(註:命令"a"為尾部追加插入,其動作是將輸入文本隊列化在記憶體中,它不會進入模式空間,而是隱含動作自動輸出模式空間內容時,在半路追上stdout並將隊列化的文本追加到其尾部,並同時輸出。下麵的"i"和"c"命令所操作都是stdout,都不會進入pattern space。也就是說,這3個命令操作的文本內容不受任何sed其他選項和命令控制。如"-n"選項無法禁止該輸入,因為"-n"禁止的是pattern space的自動輸出,同時,如果SCRIPT中這3個命令後還有後續的命令序列,則這些命令也無法匹配和操作這些文本內容,例如"p"命令不會輸出這些隊列化文本,因為它不是pattern space中的內容。
這是sed程式中少見的幾個在pattern space外處理數據的動作。具體用法如下兩個示例:

[root@xuexi tmp]# cat set2.txt
carrot
cookiee
gold
[root@xuexi tmp]# sed '/carrot/a\iron\nsteel' set2.txt  # 換行插入
carrot
iron
steel
cookiee
gold
[root@xuexi tmp]# sed '/carrot/a\iron\  # iron作為第一行,其後跟\繼續輸入下一行
> steel' set2.txt         # 直到遇到結束引號,隊列化文本才結束
carrot
iron
steel
cookiee
gold

)

'i\'
'TEXT'
是GNU的擴展功能,該命令可以在兩個地址格式的定址表達式後使用。

立即輸出該命令後的文本(除了最後一行,每一行都以反斜線"\"結尾,但在輸出時會自動移除)。

(註:"i"命令同"a"命令一樣,只不過是在stdout流的頭部加上隊列化的文本,並同時輸出。它同樣不進入pattern space,操作的也是stdout流)

'c\'
'TEXT'
刪除從模式空間輸出的內容,併在最後一行處(如果沒有指定定址選項則每一行)輸出"c"命令後隊列化文本(除了最後一行,每行都以"\"結尾,輸出時會移除)。該命令處理完後會開始新的sed迴圈,因為模式空間的內容將會被刪除。
(註:"c"即change,表示使用隊列化文本修改輸出流,雖說是修改,但實際上是替換模式空間的輸出流並輸出。它和"i"、"a"兩個命令不一樣,它"冒充"了輸出流輸出後就立即進入下一個sed迴圈,使得SCRIPT中後續的命令不會執行,而"i"、"a"則不會退出迴圈,而是會繼續回到SCRIPT迴圈中執行後續的命令)

(註:雖然這3個命令用的遠不如"s"命令頻繁,但我個人認為,理解了這3個命令,就理解了大半sed的工作機制。)

'='
是GNU擴展功能,可以在兩個地址格式的定址表達式後使用。

該命令用於輸出當前正在處理的輸入流中的行號(行號後會加上尾隨換行符)。
(註:這是將sed內部保存的行號計數器的值輸出,是記憶體中的內容,因此不進入pattern space,不受"-n"選項影響。)

'l N'
將模式空間中的內容以一種明確的形式列印:非列印字元(和"\"字元)被列印成C語言風格的轉義形式;長行被分割後列印,使用"\"字元來表示分割的位置;每一行的結尾被標記上"$"符。

N指定了行分割時期望的行長度(即多少字元換行),長度指定為0表示不換行。如果忽略N,則使用預設長度(預設70)。N參數是GNU sed的擴展功能。

'r FILENAME'
GNU擴展功能,該命令接受兩個地址的定址表達式。

讀取filename中的內容並按行隊列化,然後在當前SCRIPT迴圈的最後或者讀取下一行前插入到output流中。註意,如果filename無法被讀取,它將被當成一個空文件而不會產生任何錯誤提示。

作為GNU sed的擴展功能,支持特殊的filename值:/dev/stdin,表示從標準輸入中讀取內容。

(註:

  • "r"命令的功能和"a"命令的作用是完全一樣的,連工作機制都完全相同,除了"r"命令是從文件中讀取隊列化文本,而"a"讀取的是命令行中提供的。

  • 從"r"命令之後到換行符或sed的引號之間的所有內容都作為"r"的文件參數。因此r命令之後如果還有命令,應當分行寫。

  • "r"命令是一次隊列化filename中的所有行,後文還有一個"R"命令是每次隊列化filename中的一行,它們都受輸出流的影響,只要有輸出流就追加到輸出流中,並開始下一輪的隊列化過程。並非sed每從輸入流中讀取一行就隊列化一次)

)

'w FILENAME'
將模式空間的內容寫入到filename中,作為一個GNU sed擴展,它支持兩種特殊的filename值:/dev/stderr和/dev/stdout,分別表示將模式空間的內容寫到標準錯誤和標準輸出中。

在輸入流的第一行被讀入前,filename文件將被創建(或截斷,即清空);所有引用相同filename的"w"命令(包括替換命令"s"後的"w"修飾符)不會先關閉filename文件再打開該文件來寫入。

(註:

  • 即sed腳本中使用了"w"命令後該filename文件對sed而言一直是處於打開狀態的,直到sed退出才關閉。多次使用引用相同文件的"w"命令會向該打開文件中寫入,因此多個"w"寫入filename時是追加寫入的方式,但如果"w"打開的文件已存在,則會先截斷該文件,即後續追加式的"w"輸出會覆蓋原文件。
  • "w"命令不會影響pattern space的輸出,它就像tee命令一樣,一份輸出到屏幕,一份重定向到filename中。

)

'D'
如果pattern space中未包含換行符,則像"d"命令中提到的一樣進入下一個sed迴圈。否則刪除pattern space中第一個換行符之前的所有內容,並重新回到SCRIPT的頂端重新對pattern space中剩下的內容進行處理,而不會讀取輸入流中的下一行。

(註:換句話說,D命令總是刪除pattern space中第一個換行符前的內容,如果刪除後pattern space中還有內容剩下,則直接回到SCRIPT頂端重新對剩下的這部分內容執行命令,即以"continue"的方式強制進入下一個SCRIPT迴圈,如果pattern space中沒有剩下內容,則直接退出當前SCRIPT迴圈,併進入下一個sed迴圈。)

'N'
在當前pattern space中的尾部加上一個換行符,並讀取輸入流的下一行追加到換行符後面。如果輸入流中沒有下一行供"N"讀取,則直接退出sed迴圈。

(註:無論是sed迴圈的第一步、還是n命令或是N命令,它們讀取下一行到pattern space時總會移除行尾換行符。但N命令在讀取下一行前在當前pattern space的尾部加上了一個"\n",這使得pattern space中有了兩行,如果一個SCRIPT中有多次N命令還可能有更多行。因此N命令是sed進入多行工作模式的方法。但要註意,雖然稱為多行模式,但在pattern space中加入換行符並非真的換行,在pattern space中仍是一行顯示的,因為它能被匹配到,且在計算字元數量時會被計入,它僅僅只是一個特殊符號,只不過在輸出時會換行顯示)

'P'
輸出pattern space中第一個換行符"\n"前面的內容。

(註:即輸出pattern space中的第一行)

'h'
使用pattern space中的內容替換hold space中的內容。 (註:替換後,pattern space內容仍然保留,不會被清空)

'H'
在hold space的尾部追加一個換行符,並將pattern space中的內容追加到hold space的換行符尾部。
(註:追加後,pattern space內容仍然保留,不會被清空)

'g'
使用hold space中的內容替換pattern space中的內容。

'G'
在當前pattern space的尾部追加一個換行符,並將hold space中的內容追加到pattern space的換行符尾部。
(註:這是另一個進入多行模式空間的方法。這就有了一個特殊的用法,sed 'G' filename可以在filename的每一行後添加一個空行,這也是很多人產生誤解的地方,認為hold space的初始內容為空行,追加到pattern space就成了空行。其實並非如此,因為無論是pattern space還是hold space在初始時都是空的,G命令在pattern space的尾部加上了換行符,並將hold space中的空內容追加到換行符後,使得這成了一個空行。)

'x'
交換pattern space和hold space的內容。

3.7 Commands for 'sed' gurus(大師級的sed命令)

在大多數情況下,使用這些命令意味著你可能可以使用像"awk"或"perl"等的工具更好地達到目的。但是偶爾有人會堅持使用'sed',這些命令可能會使得腳本變得非常複雜。

(註:雖說標簽功能很少使用,但有時候確實非常有用,它在sed腳本內部實現了簡單的編程結構體)

':LABEL'
不可接在定址表達式後。

設置一個分支標簽LABEL供"b"、"t"或"T"(註:"T"命令在後文的GNU sed擴展中說明)命令跳轉。除此功能,無任何作用。
(註:冒號和LABEL之間不能有空格,雖然原文中有空格,但這是錯誤的)

'b LABEL'
無條件跳轉到標簽LABEL上。如果LABEL參數省略,則跳轉到SCRIPT的尾部準備進入下一個sed迴圈,即跳轉到隱含動作auto_print的前面。

't LABEL'
如果對最近讀入的行有"s"命令的替換成功了,則跳轉到LABEL標簽處。如果LABEL參數省略,則跳轉到SCRIPT的尾部準備進入下一個sed迴圈,即跳轉到隱含動作auto_print的前面。(註:該命令和"T LABEL"相反,"T"是在"s"替換不成功時跳轉到指定標簽。)

(註:另外,"t"的跳轉條件是有"s"命令替換成功,不是最近一個"s"命令替換成功,所以如果有多個"s"命令,即使"t"命令最近的"s"命令不成功,則仍會跳轉,直到一個跳轉迴圈內都沒有替換成功的才終止跳轉。例如:

`echo 'abcdef' | sed ':x;s/haha/yyy/;s/def/haha/;s/yyy/zzz/;tx'`
abczzz

第一輪,被讀取的輸入行在第一個"s"和第3個"s"命令失敗,但第二個成功,所以仍會執行跳轉,於是進入第二輪。在第二輪,第一個"s"和第三個"s"替換成功,所以繼續跳轉,於是進入第三輪。在第三輪中,所有"s"都失敗,所以不再跳轉。

)

(註:篇幅原因,使用一個示例簡單解釋一下標簽和跳轉命令的使用。

  1. 使用":LABEL"設置好標簽後,就可以使用"b LABEL"、"t LABEL"或"T LABEL"命令跳轉到該標簽。
  2. 跳轉到某標簽的意思是開始執行該標簽後的命令。
  3. 如果"b"、"t"或"T"命令省略了參數LABEL,則跳轉到隱藏標簽,即自動輸出pattern space動作auto_print的前面。

假設有如下test.txt文件,該文件中空白處都是一個個的空格。目的是多個連續的空格替換成圓圈"○",有幾個空格就替換成幾個圓圈,但初始時只有一個空格的不進行替換(即第一行的第一個空格和最後一行的最後一個空格不替換)。

[root@xuexi ~]# cat test.txt
 sleep       sleep
  sleep      sleep
   sleep     sleep
    sleep    sleep
     sleep   sleep
      sleep  sleep
       sleep sleep

可以使用下麵簡單的命令來實現:

[root@xuexi ~]# sed 's/  /○○/g;s/○ /○○/g' test.txt
 sleep○○○○○○○sleep
○○sleep○○○○○○sleep
○○○sleep○○○○○sleep
○○○○sleep○○○○sleep
○○○○○sleep○○○sleep
○○○○○○sleep○○sleep
○○○○○○○sleep sleep

如果使用標簽功能,就可以變相地實現sed命令組的迴圈和條件判斷功能。例如使用"b"標簽跳轉功能來實現,語句如下:

[root@xuexi ~]# sed '
:replace;
s/  /○○/;
/  /b replace;
s/○ /○○/g' test.txt

該語句首先定義了一個標簽replace,在其後是第一個要執行的命令,作用是將兩個空格替換成兩個圓圈的"s"命令,隨後是"b"標簽跳轉語句,表示如果行中有兩個連續的空格,就使用"b"命令跳轉到replace標簽處,於是再次執行"s"命令,替換結束後再

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原因: 當我們通過request獲取客戶端IP時,自身伺服器通常會為了保護信息或者負載均衡的目的,對自身伺服器做反向代理。此時如果我們通過request.getRemoteAddr();可能獲取到的是自身代理伺服器的IP,而無法達到獲取用戶請求ip的目的。 解決辦法: 以下整理了各個代理伺服器自己開 ...
  • 原文發表於cu:2016-06-14 Zabbix觸發器(trigger)達到閥值後會有動作(action)執行:發送告警信息或執行遠程命令。 本文主要配置驗證zabbix執行遠程命令。 一.環境 Server:基於CentOS-7-x86_64-1511; Zabbix:zabbix-3.0.1s ...
  • 伺服器說明 外網 IP(NAT) 內網 IP(NAT) 主機名apache web 伺服器 10.0.0.7/24 172.16.1.7/24 web02nginx web 伺服器 10.0.0.8/24 172.16.1.8/24 web01NFS 存儲伺服器 10.0.0.31/24 172.1 ...
  • 編輯 ...
  • 綠色文件 可執行文件,可執行的程式 紅色文件 壓縮文件或者包文件 藍色文件 目錄 www.2cto.com 白色文件 一般性文件,如文本文件,配置文件,源碼文件等 淺藍色文件 鏈接文件,主要是使用ln命令建立的文件 ...
  • 作為一個運維 不是你懂多少知識才是你的價值 你有幸能遇到多少錯誤才是你的最大的價值 知識 你有我有大家有 錯誤我有你沒有 這便是我的價值 我遇到一個錯誤 蠻難遇到的一個錯誤 所以想分享給大家 下麵我在模擬機演示給大家 用 root許可權 避免你們說是因為許可權的錯誤 2017年9月5日 我在切換sftp ...
  • 1 什麼是TLS 原理在網上資料很多,這裡不展開。 簡單點說,動態申請的每線程變數。有一類比較熟悉的每線程變數是一個帶__thread的每線程變數,兩者的區別在於,TLS這類每線程變數是動態申請的。有以下一系列介面: #include <pthread.h> int pthread_key_crea ...
  • 說明:文章所有內容截選自實驗樓教程 "【Vim編輯器】" ~ Vim 快速入門 一、實驗介紹 1.1 實驗內容 本次實驗將學習vim中的不同模式和一些基本操作。 1.2 實驗知識點 + Vim中的六種基本模式 + Vim中的基本操作 1.3 課程來源 "VIM 線上手冊" 二、實驗步驟 2.1 vi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...