sed篇總共分成6章:(簡(jiǎn)書(shū)版)
上一篇中介紹了N/D/P三個(gè)命令燕侠,它們可以形成多行的模式空間穗慕,在一點(diǎn)程度上彌補(bǔ)了單行模式空間的不足俺榆,我們用sed編輯文本的能力又進(jìn)了一步。模式空間是sed內(nèi)部維護(hù)的一個(gè)緩存空間蝠猬,它存放著讀入的一行或者多行內(nèi)容。但是模式空間的一個(gè)限制是無(wú)法保存模式空間中被處理的行,因此sed又引入了另外一個(gè)緩存空間——模式空間(Hold Space)辆雾。
保持空間
保持空間用于保存模式空間的內(nèi)容万伤,模式空間的內(nèi)容可以復(fù)制到保持空間窒悔,同樣地保持空間的內(nèi)容可以復(fù)制回模式空間。sed提供了幾組命令用來(lái)完成復(fù)制的工作敌买,其它命令無(wú)法匹配也不能修改模式空間的內(nèi)容简珠。
操作保持空間的命令如下所示:
名稱命令說(shuō)明
保存(Hold)h/H將模式空間的內(nèi)容復(fù)制或者追加到保持空間
取回(Get)g/G將保持空間的內(nèi)容復(fù)制或者追加到模式空間
交換(Exchange)x交換模式空間和保持空間的內(nèi)容
這幾組命令提供了保存、取回以及交換三個(gè)動(dòng)作,交換命令比較容易理解聋庵,保存命令和取回命令都有大寫(xiě)和小寫(xiě)兩種形式膘融,這兩種形式的區(qū)別是小寫(xiě)的是將會(huì)覆蓋目的空間的內(nèi)容,而大寫(xiě)的是將內(nèi)容追加到目的空間祭玉,追加的內(nèi)容和原有的內(nèi)容是以\n分隔氧映。
基本使用
我們隨便試試這幾個(gè)命令,假設(shè)有如下測(cè)試文本:
$ cat text121122111222
1. 首先脱货,僅使用h/H或者g/G命令:
使用h命令:
$ sed'h'text121122111222
使用G命令:
$ sed'G'text121122111222
前者返回的結(jié)果正常岛都,因?yàn)閺?fù)制到保持空間的內(nèi)容并沒(méi)有取回;后者每一行的后面都多了一個(gè)空行振峻,原因是每行都會(huì)從保持空間取回一行疗绣,追加(大寫(xiě)的G)到模式空間的內(nèi)容之后,以\n分隔铺韧。
2. 使用x命令交換空間
$ sed'x'text121122111
命令執(zhí)行后多矮,發(fā)現(xiàn)前面多了一個(gè)空行并且最后一行不見(jiàn)了。我在前面一直強(qiáng)調(diào)sed命令用好哈打,要有用大腦回顧命令執(zhí)行過(guò)程的能力:
* 當(dāng)讀入第一行的時(shí)候塔逃,模式空間中的內(nèi)容是第一行的內(nèi)容,而保持空間是空的料仗,這個(gè)時(shí)候交換兩個(gè)空間湾盗,導(dǎo)致模式空間為空,保持空間為第一行的內(nèi)容立轧,因此輸出為空行格粪;
* 當(dāng)讀入下一行之后,模式空間為第2行的內(nèi)容氛改,保持空間為第一行的內(nèi)容帐萎,交換后輸出第1行的內(nèi)容;
* 依次讀入每一行胜卤,輸出上一行的內(nèi)容疆导;
* 直到最后一行被讀入到模式空間,交換后輸出倒數(shù)第二行的內(nèi)容葛躏,而最后一行的內(nèi)容并沒(méi)有輸出澈段,此時(shí)命令執(zhí)行結(jié)束。
深入使用
上面的例子簡(jiǎn)單地介紹了保持空間命令的基本使用方法舰攒,這些命令單個(gè)使用可能效果不大败富,但是組合起來(lái)的效果是非常好的。
1.第一個(gè)例子: 使用逗號(hào)拼接行
$ sed'H;$!d;${x;s/^\n//;s/\n/,/g}'text1,11,2,11,22,111,222
上面的命令執(zhí)行過(guò)程是這樣的摩窃,使用H將每一行都追加到保持空間兽叮,這里利用d命令打斷常規(guī)的命令執(zhí)行流程,讓sed繼續(xù)讀入新的一行,直接到將最后一行都放到保持空間充择。這個(gè)時(shí)候使用x命令將保持空間的內(nèi)容交換到模式空間德玫,模式空間的內(nèi)容現(xiàn)在是這樣的:\n1\n11\n2\n11\n22\n111\n222匪蟀。替換的步驟分成兩個(gè)椎麦,首先去掉首個(gè)回車符,然后把剩余的回車符替換成逗號(hào)材彪。
其實(shí)上面的過(guò)程可以包裝成一個(gè)常用的函數(shù):
$functionjoin_lines()>{>sed'H;$!d;${x;s/^\n//;s/\n/,/g}'$1>}$ join_lines text1,11,2,11,22,111,222
進(jìn)一步观挎,我們可以讓分隔符可以通過(guò)參數(shù)設(shè)置,另外一方面刪除文件名的參數(shù)段化,而改成常見(jiàn)的過(guò)濾器命令形式(即通過(guò)管道傳遞輸入):
$functionjoin_lines()>{>localdelim=${1:-,}>sed'H;$!d;${x;s/^\n//;s/\n/'$delim'/g}'>}$ cat text|join_lines';'1;11;2;11;22;111;222
但是如果我們要用&作為符號(hào)嘁捷,就會(huì)出現(xiàn)問(wèn)題:
$ cat text|join_lines'&'11121122111222
上面并沒(méi)有&作為分隔符,這是因?yàn)?amp;是元字符显熏,表示匹配的部分雄嚣,這里剛好是回車符\n。因此我們需要對(duì)分隔符進(jìn)行轉(zhuǎn)義:
$functionjoin_lines()>{>localdelim=${1:-,}>sed'H;$!d;${x;s/^\n//;s/\n/\'$delim'/g}'
> }
$ cat text | join_lines '&'
1&11&2&11&22&111&222
2.第二個(gè)例子:將語(yǔ)句中的特定單詞轉(zhuǎn)換成大寫(xiě)
現(xiàn)在有這樣的文本喘蟆,有許多類似這樣的find the Match statement語(yǔ)句缓升,其中Match是語(yǔ)句的名稱,但是這個(gè)單詞的大小寫(xiě)不統(tǒng)一蕴轨,有些地方是小寫(xiě)的港谊,有些地方是首字符大寫(xiě),現(xiàn)在我們要做的是把這個(gè)單詞統(tǒng)一轉(zhuǎn)換成大寫(xiě)橙弱。
容易聯(lián)想到的是Sed&awk筆記之sed篇:基礎(chǔ)命令中介紹的y命令歧寺,利用y命令確實(shí)可以做到小寫(xiě)轉(zhuǎn)換成大寫(xiě),轉(zhuǎn)換的命令是這樣的:
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
但是y命令是會(huì)把模式空間的所有內(nèi)容都轉(zhuǎn)換棘脐,這樣不能滿足我們的需求斜筐。但是我們可以利用保持空間保存當(dāng)前行,然后處理模式空間中的內(nèi)容:
/the.*statement/{h
s/.*the \(.*\) statement.*/\1/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/G
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/}
老規(guī)矩一條一條過(guò)上面的命令蛀缝,為了方便說(shuō)明奴艾,每個(gè)命令解釋后我都會(huì)給出當(dāng)前模式空間和保持空間的內(nèi)容。
首先找到需要處理的行(假設(shè)當(dāng)前行為find the Match statement)内斯。
Pattern space: find the Match statement
將當(dāng)前行保存到保持空間:
Pattern space: find the Match statement
Hold space: find the Match statement
然后利用替換命令獲取需要處理的單詞:
Pattern space: Match
Hold space: find the Match statement
然后通過(guò)轉(zhuǎn)換命令將其轉(zhuǎn)換成大寫(xiě):
Pattern space: MATCH
Hold space: find the Match statement
現(xiàn)在再利用G命令將保持空間的內(nèi)容追加到模式空間最后:
Pattern space: MATCH\nfind the Match statement
Hold space: find the Match statement
最后再次利用替換命令處理下:
Pattern space: find the MATCH statement
Hold space: find the Match statement
流程控制
一般情況下蕴潦,sed是將編輯命令從上到下依次應(yīng)用到讀入的行上,但是像d/n/D/N命令都能夠在一定程度上改變默認(rèn)的執(zhí)行流程俘闯,甚至利用N/D/P三個(gè)命令可以形成一個(gè)強(qiáng)大的循環(huán)處理流程潭苞。除此之外,其實(shí)sed還提供了分支命令(b)和測(cè)試(test)兩個(gè)命令來(lái)控制流程真朗,這兩個(gè)命令可以跳轉(zhuǎn)到指定的標(biāo)簽(label)位置繼續(xù)執(zhí)行命令此疹。標(biāo)簽是以冒號(hào)開(kāi)頭的標(biāo)記,如下例中的:top標(biāo)簽:
:top
command1
command2/pattern/b top
command3
當(dāng)執(zhí)行到/pattern/b top時(shí),如果匹配pattern蝗碎,則跳轉(zhuǎn)到:top標(biāo)簽所在的位置湖笨,繼續(xù)執(zhí)行下一個(gè)命令command1。
如果沒(méi)有指定標(biāo)簽蹦骑,則將控制轉(zhuǎn)移到腳本的結(jié)尾處慈省。也許這只是一個(gè)默認(rèn)的行為,但是有時(shí)候如果用得好也是非常有用的眠菇,例如:
/pattern/b
command1command2command3
當(dāng)執(zhí)行到/pattern/b時(shí)边败,如果匹配pattern,則跳轉(zhuǎn)到最后捎废。這種情況下匹配pattern的行可以避開(kāi)執(zhí)行后續(xù)的命令笑窜,被排除在外。
下一個(gè)例子中登疗,我們利用分支命令的跳轉(zhuǎn)效果達(dá)到類似if語(yǔ)句的效果:
command1/pattern/b end
command2:end
command3
當(dāng)執(zhí)行到/pattern/b end時(shí)排截,如果匹配pattern,則跳轉(zhuǎn)到:end標(biāo)簽所在的位置辐益,跳過(guò)command2而不執(zhí)行断傲。
進(jìn)一步地,利用兩個(gè)分支命令可以達(dá)到if..else的分支效果:
command1/pattern/b dothree
command2
b:dothree
command3
這個(gè)例子中荷腊,當(dāng)執(zhí)行到/pattern/b dothree時(shí)艳悔,若匹配pattern則中轉(zhuǎn)到:dothree標(biāo)簽,此時(shí)執(zhí)行command3女仰;若不匹配猜年,則執(zhí)行command2,并且跳轉(zhuǎn)到最后疾忍。
上面的例子都是用到了分支命令乔外,分支命令的跳轉(zhuǎn)是無(wú)條件的。而與之相對(duì)的是測(cè)試命令一罩,測(cè)試命令的跳轉(zhuǎn)是有條件的杨幼,當(dāng)且僅當(dāng)當(dāng)前行發(fā)生成功的替換時(shí)才跳轉(zhuǎn)。
為了說(shuō)明測(cè)試命令的用法聂渊,我們用它來(lái)實(shí)現(xiàn)前文定義過(guò)的join_lines函數(shù):
$ sed':a;$!N;s/\n/,/;ta'text1,11,2,11,22,111,222
在最前面我們添加了一個(gè)標(biāo)簽:a差购,然后在再最后面利用測(cè)試命令跳轉(zhuǎn)到該標(biāo)簽『核裕可能欲逃,你會(huì)覺(jué)得這里也可以用分支命令,但是事實(shí)上分支命令會(huì)導(dǎo)致死循環(huán)饼暑,因?yàn)樵谒锼麤](méi)有結(jié)束的條件稳析。
但是測(cè)試命令就不同了洗做,這一點(diǎn)直到最后才體現(xiàn)出來(lái)。當(dāng)最后一行被N命令讀入之后彰居,回車替換成逗號(hào)诚纸,此時(shí)ta繼續(xù)跳轉(zhuǎn)到最開(kāi)頭,因?yàn)樗行卸家呀?jīng)被讀入陈惰,所以$!不匹配畦徘,同時(shí)模式空間中的回車已經(jīng)全部被替換成逗號(hào),所以替換也不會(huì)發(fā)生奴潘。之前我們說(shuō)過(guò)旧烧,當(dāng)且僅當(dāng)當(dāng)前行發(fā)生成功的替換時(shí)測(cè)試命令才跳轉(zhuǎn)影钉。所以此時(shí)跳轉(zhuǎn)不會(huì)發(fā)生画髓,退出sed命令。
我們可以利用sedsed這個(gè)工具來(lái)驗(yàn)證上面的過(guò)程平委,sedsed可以用來(lái)調(diào)試sed腳本奈虾。
$./sedsed-d-e':a;$!N;s/\n/,/;ta'text
PATT:1$
HOLD:$
COMM::a
COMM:$!N
PATT:1\n11$
HOLD:$
COMM:s/\n/,/PATT:1,11$
HOLD:$
COMM:t a
COMM:$!N
PATT:1,11\n2$
HOLD:$......COMM:$!N
PATT:1,11,2,11,22,111,222\n1111$
HOLD:$
COMM:s/\n/,/PATT:1,11,2,11,22,111,222,1111$
HOLD:$
COMM:t a
COMM:$!N
PATT:1,11,2,11,22,111,222,1111$
HOLD:$
COMM:s/\n/,/PATT:1,11,2,11,22,111,222,1111$
HOLD:$
COMM:t a
PATT:1,11,2,11,22,111,222,1111$
HOLD:$1,11,2,11,22,111,222,1111
看第27行替換命令發(fā)生的時(shí)候,此時(shí)模式空間的內(nèi)容為PATT:1,11,2,11,22,111,222,1111$廉赔,因此替換失敗肉微,ta命令不會(huì)發(fā)生跳轉(zhuǎn),腳本執(zhí)行退出蜡塌。
而如果在這里把測(cè)試命令換成分支命令碉纳,整個(gè)執(zhí)行過(guò)程就會(huì)陷入死循環(huán)了:
COMM:b a
COMM:$!N
PATT:1,11,2,11,22,111,222,1111$
HOLD:$
COMM:s/\n/,/PATT:1,11,2,11,22,111,222,1111$
HOLD:$
COMM:b a
COMM:$!N
PATT:1,11,2,11,22,111,222,1111$
高級(jí)命令總結(jié)
到此為止,所有高級(jí)命令的用法就已經(jīng)介紹完了馏艾。最后一段內(nèi)容由于時(shí)間的關(guān)系劳曹,寫(xiě)得比較倉(cāng)促。高級(jí)命令的用法比起基礎(chǔ)命令相對(duì)復(fù)雜一點(diǎn)琅摩,而且容易出錯(cuò)铁孵,需要十分小心,如果不確定可以用上面介紹的sedsed工具來(lái)調(diào)式下房资,而且便于加深各種命令行為的理解蜕劝。
轉(zhuǎn)自:團(tuán)子的小窩, 本文固定鏈接:Sed and awk 筆記之 sed 篇:高級(jí)命令(二)