sed是一個流式編輯器程序,它讀取輸入流(能夠是文件、標準輸入)的每一行放進模式空間(pattern space),同時將此行行號經過sed行號計數器記錄在內存中,而後對模式空間中的行進行模式匹配,若是能匹配上則使用sed程序內部的命令進行處理,處理結束後,從模式空間中輸出(默認)出去,並清空模式空間,隨後再從輸入流中讀取下一行到模式空間中進行相同的操做,直到輸入流中的全部行都處理完成。因而可知,sed是一個循環一個循環處理內容的。html
這是sed的一個循環的過程:面試
(注:(如看不懂,請跳過)若是是讀取文件數據,則會每次須要的時候一次性加載必定量(好比多行)的數據到os buffer,而後sed從os buffer中一行一行讀取,並非要讀一行就從磁盤文件中加載一行。另外,若是是管道或其它輸入流,則直接從對應的緩存中一行一行讀取。驗證命令:sed ‘p;s/.*/:>filename/e;d’ filename)正則表達式
上述整個循環過程當中,第2步是咱們寫sed命令所修改的地方,其他的幾個步驟,經過命令行沒法改變。可是,sed有幾個命令和選項能改變第三、4步的行爲,使其輸出老是輸出空內容或沒法清空模式空間。shell
若是使用編程結構來描述,則大體過程以下:編程
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; execute cmd3 in SCRIPT; …… auto_print; remove_pattern_space; done done
其中while循環執行的正是SCRIPT中的全部命令,只不過通常狀況下,while循環只執行一輪就退出並進入外層的for循環。因而,外層的for循環稱之爲"sed循環",內層的while循環稱之爲"SCRIPT"循環。因此,for循環只包含了兩個動做:讀取下一行和執行SCRIPT循環。緩存
其實while循環中是有continue、break甚至是exit的,分別表示回到SCRIPT的頂端(即進入下一個SCRIPT循環)、退出當前SCRIPT循環回到外層sed循環以及退出整個sed循環。顯然,這不是"花拳繡腿"的內容。bash
最後,說明下sed命令行如何書寫,其實就是寫SCRIPT部分,這部分的寫法比較靈活,大體有如下幾種:網絡
# 一行式。多個命令使用分號分隔 sed Address{cmd1;cmd2;cmd3...} # 多個表達式時,可使用"-e"選項,也能夠不用,但使用分號分隔 sed Address1{cmd1;cmd2;cmd3};Address2{cmd1;cmd2;cmd3}... sed -e 'Address1{cmd1;cmd2;cmd3}' -e 'Address2{cmd1;cmd2;cmd3}' ... # 分行寫時 sed Address1{ cmd1 cmd2 cmd3 } Address2{ cmd1 cmd2 cmd3 }
若是是寫在文件中,即sed腳本,以文件名爲a.sed爲例。app
#!/usr/bin/sed -f #註釋行 Address1{cmd1;cmd2...} Address2{cmd1;cmd2...} ......
其中cmd部分還能夠進行模式匹配,也即相似於"Address{{pattern1}cmd1;{pattern2}cmd2}"的寫法。例如, /^abc/{2d;p} 。編輯器
有了以上基本的大綱性知識,理解和深刻sed機制就簡單多了。
sed選項不算多,能用到的更沒幾個。
sed OPTIONS SCRIPT INPUT_STREAM
可能用到的幾個選項:
'-n'
默認狀況下,sed將在每輪script循環結束時自動輸出模式空間中的內容。使用該選項後可使得此次自動輸出動做輸出空內容,而不是當前模式空間中的內容。注意,"-n"是輸出空內容而不是禁用輸出動做,雖然二者的結果都是不輸出任何內容,但在有些依賴於輸出動做和輸出流的地方,它們的區別是很大的,前者有輸出流,只是輸出空流,後者則沒有輸出流。
'-e SCRIPT'
前文說了,SCRIPT中包含的是命令的集合,"-e"選項就是向SCRIPT中添加命令的。能夠省略"-e"選項,但若是命令行容易產生歧義,則使用"-e"選項可明確說明這部分是SCRIPT中的命令。另外,若是一個"-e"選項不方便描述所需命令集合時,能夠指定多個"-e"選項。
'-f SCRIPT-FILE'
指定包含命令集合的SCRIPT文件,讓sed根據SCRIPT文件中的命令集處理輸入流。
'-i[SUFFIX]'
該選項指定要將sed的輸出結果保存(覆蓋的方式)到當前編輯的文件中。GNU sed是經過建立一個臨時文件並將輸入寫入到該臨時文件,而後重命名爲源文件來實現的。
噹噹前輸入流處理結束後,臨時文件被重命名爲源文件的名稱。若是還提供了SUFFIX,則在重命名臨時文件以前,先使用該SUFFIX修改源文件名,從而生成一個源文件的備份文件。
臨時文件老是會被重命名爲源文件名稱,也就是說輸入流處理結束後,仍使用源文件名的文件是sed修改後的文件。文件名中包含了SUFFIX的文件則是最原始文件的備份。例如源文件爲a.txt,sed -i'.log' SCRIPT a.txt
將生成兩個文件:a.txt和a.txt.log,前者是sed修改後的文件,a.txt.log是源a.txt的備份文件。
重命名的規則以下:若是擴展名不包含符號"",將SUFFIX添加到原文件名的後面看成文件後綴;若是SUFFIX中包含了一個或多個字符"",則每一個"*"都替換爲原文件名。這使得你能夠爲備份文件添加一個前綴,而不是後綴。若是沒有提供SUFFIX,源文件被覆蓋,且不會生成備份文件。
該選項隱含了"-s"選項。
'-r'
使用擴展正則表達式,而不是使用默認的基礎正則表達式。sed所支持的擴展正則表達式和egrep
同樣。使用擴展正則表達式顯得更簡潔,由於有些元字符不用再使用反斜線"\"。正則表達式見grep命令中文手冊。
'-s'
默認狀況下,若是爲sed指定了多個輸入文件,如sed OPTIONS SCRIPT file1 file2 file3
,則多個文件會被sed看成一個長的輸入流,也就是說全部文件被當成一個大文件。指定該選項後,sed將認爲命令行中給定的每一個文件都是獨立的輸入流。
既然是獨立的輸入流,範圍定址(如/abc/,/def/)就沒法跨越多個文件進行匹配,行號也會在處理每一個文件時重置,"$"表明的也將是每一個文件的最後一行。這也意味着,若是不使用該選項,則這幾個行爲都是能夠完成的。
示例:以sed命令"p"和"="爲例,其中"p"命令用於強制輸出當前模式空間中的內容,"="命令用於輸出sed行號計數器當前的值,即剛被讀入到模式空間中的行是輸入流中的第幾行。
(1).只輸出a.txt中的第5行。
sed -n 5p a.txt
這裏使用了"-n"選項,使得讀取到模式空間的每一行都沒法被輸出,只有明確使用了"p"選項才能被"p"動做輸出。因爲只有讀入的第5行內容能匹配"5",才能被"p"輸出。
其實上面的命令和sed -n -e '5p' a.txt
是徹底同樣的,由於"5p"在sed解析命令行時不會產生歧義,因此能夠省略"-e"選項。
(2).輸出a.txt,並輸出每行的行號。
sed '=' a.txt
因爲要輸出a.txt的內容,因此不使用"-n"選項,同時"="命令會輸出每行行號。
(3).分別輸出a.txt和b.txt的第5行,並分別保存到".bak"後綴的文件中。
sed -i'*.bak' -n '5p' a.txt b.txt
此處必須使用"-s"選項,不然將只會輸出"a.txt+b.txt"結合後的第5行。但"-i"隱含了"-s"選項。這會生成4個文件:a.txt、b.txt和a.txt.bak、b.txt.bak。前兩個是第5行內容,後兩個是源文件的備份文件。
(4).使用擴展正則表達式,輸出a.txt和b.txt中能包含3個以上字母"a"的行。
sed -r -n '/aaa+/p' a.txt b.txt
當sed將輸入流中的行讀取到模式空間後,就須要對模式空間中的內容進行匹配,若是能匹配就能執行對應的命令,若是不能匹配就直接輸出、清空模式空間並進入下一個sed循環讀取下一行。
匹配的過程稱爲定址。定址表達式有多種,但總的來講,其格式爲[ADDR1][,ADDR2]
。這能夠分爲3種方式:
不管是ADDR1仍是ADDR2,均可以使用兩種方式進行匹配:行號和正則表達式。以下:
'N'
指定一個行號,sed將只匹配該行。(須要注意,除非使用了"-s"或"-i"選項,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",則匹配的是每一個文件的最後一行。總之,"$"匹配的是每一個輸入流的最後一行。
須要注意的是,sed採用行號計數器來臨時記錄當前行的行號,所以sed在讀取到最後一行前即便是倒數第二行的時候,徹底不知道最後一行是第幾行,因此表明最後一行的"$"沒法進行任何數學運算,例如倒數第二行使用"$-1"表示是錯誤的。並且,"$"只是一個額外的標記符號,當sed讀取到輸入流的最後一行時,發現這就是最後一行,因而爲此行打上"$"記號,並讀取到模式空間中。
'/REGEXP/'
將選擇能被正則表達式REGEXP匹配的全部行。若是REGEXP中自身包含了字符"/",則必須使用反斜線轉義,即"\/"
。
'/REGEXP/I'
和"/REGEXP/"是同樣的,只不過匹配的時候不區分大小寫。
'\%REGEXP%'
('%'可使用其餘任意單個字符替換。) 這和上一個定址表達式的做用是同樣的,只不過是使用符號"%"替換了符號"/"。當REGEXP中包含"/"符號時,使用該定址表達式就無需對"/"使用反斜線"\"轉義。但若是此時REGEXP中包含了"%"符號時,該符號須要使用"\"轉義。
總之,定址表達式中使用的分隔符在REGEXP中出現時,都須要使用反斜線轉義。
'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行。
另外,在定址表達式的後面加"!"符號表示反轉匹配的含義。也就是說那些匹配的行將不被選擇,而是不匹配的行被選擇。
例如,如下幾個定址的示例:
1 2 3 4 5 sed -n '3p' INPUTFILE sed -n '3,5!p' INPUTFILE sed -n '3,/^# .*/! p' INPUTFILE sed -n '/abc/,/xyz/p' INPUTFILE sed -n '!p' INPUTFILE # 這個有悖常理,但確實是容許的
sed命令不少,本文的只簡單介紹幾個最多見的。
此處不以命令的用法爲重,而是經過這幾個命令,引出sed最重要的原理和執行機制(還包括本文的第一節內容),併爲閱讀下一篇文章sed武功心法:info sed打下基礎。並且理解了這些原理,再使用sed作任何操做都有理可循,遇到疑難之處也知道如何進行分析。而這,是任何書籍(包括廣爲推崇的sed && awk)、教學視頻和網絡文章中都沒有的內容(至少我從未見過,這些內容也是我花費大量精力通過大量實驗推演出來)。
(1).強制輸出命令"p"。
該命令能強制輸出當前模式空間的內容。即便使用了"-n"選項。
事實上,它們本就不衝突,由於循環過程以下:
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; ADDR1,ADDR2{print}; # "p" command …… auto_print; remove_pattern_space; done done
在sed處理的過程當中,"p"和"auto_print"是兩個輸出動做,都是輸出當前模式空間的內容,只不過auto_print是隱含動做。使用了"-n"選項,其所影響的動做僅是"auto_print",使其輸出空內容。也所以,當沒有使用"-n"選項時,模式空間的內容會被輸出兩次。
例如,僅輸出標準輸入的第2行內容。
[root@xuexi ~]# echo -e 'abc\nxyz' | sed -n 2p xyz
不加"-n"選項,在"p"輸出以後,SCRIPT循環的結尾處還會被auto_print輸出一次。
[root@xuexi ~]# echo -e 'abc\nxyz' | sed 2p abc xyz # 這是p命令輸出的結果 xyz # 這是自動輸出的結果
(2).刪除命令"d"。
命令"d"用於刪除整個模式空間中的內容,並當即退出當前SCRIPT循環,進入下一個sed循環,即讀取下一行。
循環大體格式以下:
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; ADDR1,ADDR2{delete;break}; # "d" command …… auto_print; remove_pattern_space; done done
惟一須要注意的一點是當即退出當前SCRIPT循環,這意味着若是"d"命令後面還有其餘的命令,則這些命令都不會執行。
例如:刪除a.txt中的第5行,並保存到原文件中。
sed -i '5d' a.txt
這裏不能使用重定向的方式保存,由於重定向是在sed命令執行前被shell執行的,因此會截斷a.txt,使得sed讀取的輸入流爲空,或者結果出乎意料以外。而"-i"選項則不會操做原文件,而是生成臨時文件並在結束時重命名爲原文件名。
刪除a.sh中包含"#"開頭的註釋行,但第一行的#!/bin/bash
不刪除。
sed '/^#/{1!d}' a.sh
若是"d"後面還有命令,在刪除模式空間後,這些命令不會執行,由於會當即退出當前SCRIPT循環。例如:
echo -e 'abc\nxyz' | sed '{/abc/d;=}' 2 xyz
其中"="這個命令用於輸出行號,可是結果並無輸出被"abc"匹配的行的行號。
(3).退出sed程序命令"q"和"Q"。
使用"q"和"Q"命令的做用是當即退出當前sed程序,使其再也不執行後面的命令,也再也不讀取後面的行。所以,在處理大文件或大量文件時,使用"q"或"Q"命令能提升很大效率。它們之間的不一樣之處在於"q"命令被執行後還會使用自動輸出動做輸出模式空間的內容,除非使用了"-n"選項。而"Q"命令則會當即退出,不會輸出模式空間內容。另外,能夠爲它們指定退出狀態碼,例如"q 1"。
使用了"q"和"Q"的sed循環結構大體以下:
# "q"命令 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; ADDR1,ADDR2{auto_print;exit}; # "q" command …… auto_print; remove_pattern_space; done done # "Q"命令 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; ADDR1,ADDR2{exit}; # "Q" command …… auto_print; remove_pattern_space; done done
例如,搜索腳本a.sh,當搜索到使用了"."或"source"命令加載環境配置腳本時就輸出並當即退出。
sed -n -r '/^[ \t]*(\.|source) /{p;q}' a.sh
(4).輸出行號命令"="。
"="命令用於輸出最近被讀取行的行號。在sed內部,使用行號計數器進行行號計數,每讀取一行,行號計數器加1。計數器的值存儲在內存中,在要求輸出行號時,直接插入在輸出流中的指定位置。因爲值是存在於內存中,而非模式空間中,所以不受"-n"選項的影響。
這是一個依賴於輸出流的命令,只要有輸出動做就會追加在該輸出流的尾部。
例如,搜索出httpd.conf中"DocumentRoot"開頭的行的行號,容許有前導空白字符。
sed -n '/^[ \t]*DocumentRoot/{p;=}' httpd.conf DocumentRoot "/var/www/html" 119
若是"="命令前沒有"p"輸出命令,且沒有使用"-n"選項,則是輸出在Document所在行的前一行,由於SCRIPT最後的自動輸出動做也有輸出流。
(5).字符一一對應替換命令"y"。
該命令和"tr"命令的映射功能同樣,都是將字符進行一一替換。
例如,將a.txt中包含大寫字母的YES、Yes等替換成小寫的yes。
sed 'y/YES/yes/' a.txt
(6).手動讀取下一行命令"n"。
在sed的循環過程當中,每一個sed循環的第一步都是讀取輸入流的下一行到模式空間中,這是咱們沒法控制的動做。但sed有讀取下一行的命令"n"。
因爲是讀取下一行,因此它會觸發自動輸出的動做,因而就有了輸出流。不只如此,還應該記住的是:只要有讀取下一行的行爲,在其真正開始讀取以前必定有隱式自動輸出的行爲。
但需注意,當沒有下一行可供"n"讀取時(例如文件的最後一行已經被讀取過了),將輸出模式空間內容後直接退出sed程序,使得"n"命令後的全部命令都不會執行,即便是那兩個隱含動做。
相應的循環結構以下:
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; ADDR1,ADDR2{ # "n" command if [ "$line" -ne "$last_line_num" ];then auto_print; remove_pattern_space; read next_line to pattern_space; else auto_print; remove_pattern_space; exit; fi }; …… auto_print; remove_pattern_space; done done
注意,是先判斷是否有下一行可讀取,再輸出和清空pattern space中的內容,因此then和else語句中都有這兩個動做。 也許感受上彷佛更應該像下面這樣的優化形式:
ADDR1,ADDR2{ # "n" command auto_print; remove_pattern_space; [ "$line" -ne "$last_line_num" ] && read next_line to pattern_space || exit; };
但事實證實並不是如此,證實過程在本文結尾。此處暫不討論這些複雜的東西,先看看"n"命令的示例。
例如,搜索a.txt中包含"redirect"字符串的行以及其下一行,並輸出。
sed -n '/redirect/{p;n;p}' a.txt
再例以下面的命令。
echo -e "abc\ndef\nxyz" | sed '/abc/{n;=;p}' abc 2 def def xyz
從結果中能夠分析出,"n"讀取下一行前輸出了"abc",而後當即讀入了下一行,因此輸出的行號是2而不是1,由於這時候行號計數器已經讀取了下一行,隨後命令"p"輸出了該模式空間的內容,輸出後還有一次自動輸出的隱含動做,因此"def"被輸出了兩次。
(7).替換命令"s"。
這是sed用的最多的命令。兩個字就能歸納其功能:替換。將匹配到的內容替換成指定的內容。
"s"命令的語法格式爲:其中"/"能夠替換成任意其餘單個字符。
s/REGEXP/REPLACEMENT/FLAGS
它使用REGEXP去匹配行,將匹配到的那部分字符替換成REPLACEMENT。FLAGS是"s"命令的修飾符,常見的有"g"、"p"和"i"或"I"。
REPLACEMENT中可使用"\N"(N是從1到9的整數)進行後向引用,所表明的是REGEXP第N個括號(...)中匹配的內容。另外,REPLACEMENT中能夠包含未轉義的"&"符號,這表示引用pattern space中被匹配的整個內容。須要注意,"&"是引用pattern space中的全部匹配,不只僅只是括號的分組匹配。
例如,刪除a.sh中全部"#"開頭(能夠包括前導空白)的註釋符號"#",但第一行"#!/bin/bash"不處理。
sed -i '2,$s/^[ \t]*#//' a.sh
爲a.sh文件中的第5行到最後一行的行首加上註釋符號"#"。
sed '5,$s/^/#/' a.sh
將a.sh中全部的"int"單詞替換成"SIGINT"。
sed 's/\bint\b/SIGINT/g' a.sh
將a.sh中"cmd1 && cmd2 || cmd3"的cmd2和cmd3命令對調個位置。
sed 's%&&\(.*\) ||\(.*\)%\&\&\2 ||\1%' a.sh
這裏使用了"%"代替"/",且在REPLACEMENT部分對"&"進行了轉義,由於該符號在REPLACEMENT中時表示的是引用REGEXP所匹配的全部內容。
(8).追加、插入和修改命令"a"、"i"、"c"。
這3個命令的格式是"[a|i|c] TEXT",表示將TEXT內容隊列化到內存中,當有輸出流或者說有輸出動做的時候,半路追上輸出流,分別追加、插入和替換到該輸出流而後輸出。追加是指追加在輸出流的尾部,插入是指插入在輸出流的首部,替換是指將整個輸出流替換掉。"c"命令和"a"、"i"命令有一絲不一樣,它替換結束後當即退出當前SCRIPT循環,並進入下一個sed循環,所以"c"命令後的命令都不會被執行。
例如:
echo -e "abc\ndef" | sed '/abc/a xyz' abc xyz def
其實"a"、"i"和"c"命令的TEXT部分寫法是比較複雜的,若是TEXT只是幾個簡單字符,如上便可。但若是要TEXT是分行文本,或者包含了引號,或者這幾個命令是寫在"{}"中的,則上面的寫法就沒法實現。須要使用符號"\"來轉義行尾符號,這表示開啓一個新行,此後輸入的內容都是TEXT,直到遇到引號或者";"開頭的行時。
例如,在a.sh的#!/bin/bash
行後添加一個註釋行"# Script filename: a.sh"以及一個空行。因爲是追加在尾部,因此使用"a"命令。
sed '\%#!/bin/bash%a\# Script filename: a.sh\n' a.sh
"a"命令後的第一個反斜線用於標記TEXT的開始,"\n"用於添加空白行。若是分行寫,或者"a"命令寫在大括號"{}"中,則格式以下:
sed '\%#!/bin/bash%a\ # Script filename: a.sh\n ' a.sh sed '\%#!/bin/bash%{p;a\ # Script filename: a.sh\n ;p}' a.sh
最後須要說的是,這3個命令的TEXT是存放在內存中的,不會進入模式空間,所以不受"-n"選項或某些命令的影響。此外,這3個命令依賴於輸出流,只要有輸出動做,無論是空輸出流仍是非空的輸出流,只要有輸出,這幾個命令就會半路"劫殺"。若是不理解這兩句話,這3個命令的結果有時可能會比較疑惑。
例如,"a"命令是追加在當前匹配行行尾的,但爲何下面的"haha"卻插入到匹配行"def"的前面去了呢?
echo -e "abc\ndef\nxyz" | sed '/def/{a\ haha ;N}' abc haha def xyz
閱讀了下面的"N"命令以後,再回頭看這個示例,應該能知道爲何。在sed修煉系列(四):sed中的疑難雜症中給出瞭解釋。
(9).多行模式命令"N"、"D"、"P"簡單說明。
在前面已經解釋了"n"、"d"和"p"命令,sed還支持它們的大寫命令"N"、"D"和"P"。
"N"、"D"和"P"命令做用很是大,它們是絕佳的組合命令,由於藉助它們能實現"窗口滑動"技術,這對於複雜的文本行操做來講大有裨益。但顯然,這不是本文的內容,在sed修煉系列(三):sed高級應用之實現窗口滑動技術中詳細說明了這3個命令的功能。
此處按照慣例,仍是給出它們的大體循環結構:其中"N"命令的if判斷和前文的"n"同樣,在本文結尾證實。
# "N"命令的大體循環結構 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; ADDR1,ADDR2{ # "N" command if [ "$line" -ne "$last_line_num" ];then lock pattern_space; auto_print; remove_pattern_space; unlock pattern_space; append "\n" to pattern_space; read next_line to pattern_space; else auto_print; remove_pattern_space; exit; fi }; …… auto_print; remove_pattern_space; done done # "D"命令的大體循環結構 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; ADDR1,ADDR2{ # "D" command delete first line in pattern_space; continue; }; …… auto_print; remove_pattern_space; done done # "P"命令的大體循環結構 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; ADDR1,ADDR2{ # "P" command print first line in pattern_space; }; …… auto_print; remove_pattern_space; done done
(10).buffer空間數據交換命令"h"、"H"、"g"、"G"、"x"簡單說明。
sed除了維護模式空間(pattern space),還維護另外一個buffer空間:保持空間(hold space)。這兩個空間初始狀態都是空的。
絕大多數時候,sed僅依靠模式空間就能達到目的,但有些複雜的數據操做則只能藉助保持空間來實現。之因此稱之爲保持空間,是由於它是暫存數據用的,除了僅有的這幾個命令外,沒有任何其餘命令能夠操做該空間,所以藉助它能實現數據的持久性。
保持空間的做用很大,它和模式空間之間的數據交換能實現不少看上去不能實現的功能,是實現sed高級功能所必須的,例如"窗口滑動"。一樣,這不是本文的內容。因此只簡單解釋這幾個命令的做用:
注意,不管是交換、追加仍是覆蓋,原空間的內容都不會被刪除。