系統(tǒng)工程師的自我修養(yǎng):sed篇
注:本文除特殊注明外均針對(duì)傳統(tǒng)UNIX中的sed,而非GNU的版本,以保證通用性,GNU的新特性本文暫不進(jìn)行介紹,請(qǐng)參看手冊(cè)。另外,本文對(duì)sed的講述和總結(jié)不為求全面,只求實(shí)用性和適用性強(qiáng)。有需要還是參閱man手冊(cè)和sed相關(guān)資料。
1.原理篇
掌握這個(gè)東西首先需要掌握的就是原理,否則一切技巧都是白扯。sed以行為處理單位,默認(rèn)輸入輸出均為系統(tǒng)標(biāo)準(zhǔn)輸入輸出(因此除非重定向,否則它并不真正修改文件),它首先判斷要處理的行是否在要處理的范圍之內(nèi)(下一章中稱之為SELECTION),如果是則讀入pattern space中,這是sed進(jìn)行字符串處理工作的一個(gè)區(qū)域。腳本中的sed命令逐條執(zhí)行來編輯pattern space里面的字符串,執(zhí)行完畢后將該pattern space中處理過的字符串進(jìn)行輸出,隨之pattern space被清空;接著,再重復(fù)執(zhí)行剛才的動(dòng)作,文件中的新的一行被讀入,判斷是否在SELECTION中,編輯、輸出,直到文件處理完畢,整個(gè)過程如下圖所示。除了 pattern space 外,sed還有一個(gè) hold space,用處是暫存文字字符串的地方,hold space中的字符串只是用于臨時(shí)處理的中間結(jié)果,是不會(huì)被輸出的(在本文第四章會(huì)有介紹,此時(shí)不了解不影響閱讀此文)。
2.用途篇
學(xué)習(xí)一個(gè)腳本語言,了解了基本原理后就要調(diào)研一下這個(gè)東東是不是滿足完成你任務(wù)的需求。一提到sed,肯定就會(huì)牽扯到awk,它具體的功能會(huì)在后邊的awk篇進(jìn)行敘述。對(duì)于同一個(gè)任務(wù),sed和awk都有可能解決,因此對(duì)于sed、awk的用途每個(gè)人都有不同的習(xí)慣和自己擅長(zhǎng)的用法,在實(shí)踐中,我個(gè)人習(xí)慣于用sed進(jìn)行行處理,也就是根據(jù)sed的原理,需要進(jìn)行一行行的處理操作時(shí)優(yōu)先使用sed。而用awk更多的進(jìn)行列處理。而在具體任務(wù)上,由于sed強(qiáng)大的替換能力和編輯能力,我常常用sed作為編輯器,而awk 作為信息獲取和格式處理輸出的能力。#p#
3.初級(jí)語法篇
作為一個(gè)腳本類工具,確定就要用它完成任務(wù)后,要開始真正使用它,掌握其語言特性是必不可少的。形式上,使用sed采用如下命令格式:
sed [options] 'SELECTION edit-instructions' file(s)
從命令格式可以看出sed可以一次處理多個(gè)文本。此處先不介紹options,因?yàn)閛ptions往往要和后邊的command進(jìn)行配合而才能體現(xiàn)出價(jià)值。下邊,我們先從command開始了解。
根據(jù)是否使用hold space(不懂這個(gè)概念也可先往下看,讀完本文就明白)的區(qū)別,對(duì)于一個(gè)初級(jí)用戶,了解如下sed中不使用hlod space 的command是實(shí)踐的第一步。本文在此基礎(chǔ)上以解決問題的思路首先來介紹不使用hold space 的命令,另外需要說明的是正則表達(dá)式是另一個(gè)配合使用的利器,在本文中為了不引入更大的麻煩,因此用例中盡量使用最簡(jiǎn)答的正則表達(dá)式。
1)范圍選取
在sed中如果不指定范圍,則處理命令是針對(duì)整個(gè)標(biāo)準(zhǔn)輸入的。如需要在某個(gè)范圍內(nèi)進(jìn)行處理,則需要進(jìn)行范圍選取。也就是命令格式中的SELSECTION。sed根據(jù)SELECTION取得相應(yīng)的文本行,在這些行中根據(jù)edit-instructions進(jìn)行編輯。注意,SELECTION和edit-instructions中的空格不是必須的。
SELECTION 可以如下表達(dá):
- 單個(gè)行號(hào):如1為取第一行,5為取第五行,$為取最后一行
- 行范圍:如5,$ 為取從第五行到最后一行之間的文本行
- 單個(gè)正則匹配:如/string/ 為取包含string的行
- 一個(gè)正則匹配范圍:如/^on/,/off$/ 為取從on開頭的行到off結(jié)尾的行之間(含這兩個(gè)匹配行)所包含的文本行。
- 行范圍與正則匹配范圍集合:如10,/man/表示從第10行到包含有man的行之間的文本
- 除去所匹配行外的范圍:如/Llew/! 表示除了匹配Llew的行外其余的文本行
在進(jìn)入處理命令前,先介紹一下本文中的幾個(gè)示例文件
phonelist:
JCHJCL01:/tmp/gnuhpc#cat phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
paths:
JCHJCL01:/tmp/gnuhpc#cat paths /opt/virtprovider/lib /var/adm/syslog /usr/bin/ /usr/local/bin
config.ini:
JCHJCL01:/tmp/gnuhpc#cat config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
2)打印命令
明確如何界定需要處理文本后,首先學(xué)習(xí)一個(gè)簡(jiǎn)單的命令:打印這部分文本。
基本語法:SELSECTIONp 打印pattern space中的內(nèi)容
Task1:打印包含F(xiàn)ranklin的行
JCHJCL01:/tmp/gnuhpc#sed -n '/Franklin/p' phonelist Franklin, Francis 704-3876
注意,選項(xiàng)-n 表示所有都不打印,而僅僅打印出匹配的行,可以試一試沒有這個(gè)選項(xiàng)的情況?;仡檚ed機(jī)制,它會(huì)將文本一行行放在pattern space,不管你做什么樣的后續(xù)操作、甚至不做任何編輯動(dòng)作,它都會(huì)在command執(zhí)行完后把pattern space打出來,這你就理解了為什么要用這個(gè)選項(xiàng)。
3)處理命令
a) 增改操作:
基本語法:
SELECTIONx\ text 其中斜杠后有回車,而x則為: i 表示插入選中行前 a 表示追加在選中行之后 c 表示將選中行修改為text
Task2:在第二行前插入一個(gè)聯(lián)系人Jonney, Wang 923-3322
JCHJCL01:/tmp/gnuhpc#sed '2i\ > Jonney, Wang 923-3322' phonelist Terrell, Terry 617-7989 Jonney, Wang 923-3322 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task3:在Martin, Marty后加入聯(lián)系人Jonney, Wang 923-3322
JCHJCL01:/tmp/gnuhpc#sed '/Martin, Marty/a\ > Jonney, Wang 923-3322' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Jonney, Wang 923-3322 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task3:將名字為L(zhǎng)lewellyn的記錄都記為“BANNED”
JCHJCL01:/tmp/gnuhpc#sed '/Llewellyn/c\ BANNED' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 BANNED Jansen, Jan 903-3333 BANNED
b)刪除操作
基本語法:
SELECTIONd ,清除pattern space中的所有內(nèi)容
Task4 刪除最后一行:
JCHJCL01:/tmp/gnuhpc#sed '$d' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333
c)替換操作:
基本語法:
'SELECTION s/old string/new string/’ 替換所選區(qū)域中第一次出現(xiàn)的old string 'SELECTION s/old string/new string/g’ 替換所選區(qū)域中所有的old string 'SELECTION y/string1/string2/’ 對(duì)所選區(qū)域中的string1所含字符對(duì)應(yīng)替換為string2中同位置的字符,與tr命令相同。
Task5:將第一個(gè)Robin替換為Robbins
JCHJCL01:/tmp/gnuhpc#sed 's/Robin/Robbins/' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robbinsson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task6:將所有Rob替換為John
JCHJCL01:/tmp/gnuhpc#sed 's/Rob/John/g' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Johninson, Johnin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task7:將/usr/bin/中的/bin/替換為/bin
JCHJCL01:/tmp/gnuhpc#sed 's/\/bin\//\/bin/' paths /opt/virtprovider/lib /var/adm/syslog /usr/bin /usr/local/bin
在這種出現(xiàn)很多/的文件時(shí)需要\來進(jìn)行轉(zhuǎn)義,稍微一多就容易出錯(cuò),那么采用如下的方式把替換分隔符的方式進(jìn)行就好,其中感嘆號(hào)只是一個(gè)其他類字符,換做另外一個(gè)字符(例如@)也是沒有關(guān)系的:
JCHJCL01:/tmp/gnuhpc#sed 's!/bin/!/bin!' paths /opt/virtprovider/lib /var/adm/syslog /usr/bin /usr/local/bin
Task8:加密所有的1234,規(guī)則為將文件中1、2、3、4對(duì)應(yīng)改為A、B、C、D:
JCHJCL01:/tmp/gnuhpc#sed 'y/1234/ABCD/' phonelist Terrell, Terry 6A7-7989 Franklin, Francis 70D-C876 Patterson, Pat 6AD-6ABB Robinson, Robin DAA-C7D5 Christopher, Chris C05-598A Martin, Marty 8AD-5587 Llewellyn, Lynn CA6-6BBA Jansen, Jan 90C-CCCC Llewellyn, Lee 8A7-88BC
d)寫文件操作:
基本語法:'SELECTION command/w filename’
Task9:將所有Rob 改為Robbin,并將結(jié)果寫到一個(gè)叫做result 的文件中
JCHJCL01:/tmp/gnuhpc#sed 's/Rob/Robbin/gw result' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robbininson, Robbinin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823 JCHJCL01:/tmp/gnuhpc#cat result Robbininson, Robbinin 411-3745
e)讀文件操作:
基本語法:'SELECTION command/r filename’
Task10:如果phonelist中存在Patterson,則將文件paths的內(nèi)容加入到Patterson后的那一行
JCHJCL01:/tmp/gnuhpc#sed '/Patterson/r paths' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 /opt/virtprovider/lib /var/adm/syslog /usr/bin/ /usr/local/bin Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
f)批處理操作:
如果要處理的很多,我們也可以將sed命令寫入一個(gè)腳本,然后運(yùn)行時(shí)采用-f選項(xiàng)指定運(yùn)行該腳本就行。請(qǐng)注意sed會(huì)將第一條命令執(zhí)行的結(jié)果發(fā)給第二條執(zhí)行,因此命令的順序尤為重要。
基本語法:sed -f scriptfile filename
Task11:把617替換為817,把704替換為522,把411替換為235
JCHJCL01:/tmp/gnuhpc#cat subscript s/617/817/ s/704/522/ s/411/235/ JCHJCL01:/tmp/gnuhpc#sed -f subscript phonelist Terrell, Terry 817-7989 Franklin, Francis 522-3876 Patterson, Pat 614-6122 Robinson, Robin 235-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
也可以使用多行操作模式,基本語法為:
'SELECTION1 operation1 … SELECTIONn operationn'
其實(shí)就是把多個(gè)命令用回車連起來
Task12:將Martin替換Mary,將Tearrey替換為Tearrey
JCHJCL01:/tmp/gnuhpc#sed 's/Martin/Mary/ s/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
另一種方式是使用-e選項(xiàng),基本語法為:
-e ‘command1’ –e ‘command2’
同樣的任務(wù):
JCHJCL01:/tmp/gnuhpc#sed -e 's/Martin/Mary/' -e 's/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
還有另外一種方法,有點(diǎn)類似于C語言中分號(hào)的使用:
JCHJCL01:/tmp/gnuhpc#sed 's/Martin/Mary/;s/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
我一般習(xí)慣于采用分號(hào),簡(jiǎn)單也夠明了。另外,對(duì)于同一個(gè)區(qū)域,還可以使用{}進(jìn)行處理:
Task13:將含有QM的行中QM改為PM,=號(hào)改為“:”
JCHJCL01:/tmp/gnuhpc#sed '/QMPath/{s/QM/PM/ > s/=/:/ > }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 PMPath: [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
#p#
4.進(jìn)階語法篇之hold space的使用
開篇提到了這個(gè)hold space,再?gòu)?fù)習(xí)一遍:Hold space 是 sed 用來暫存 pattern space 內(nèi)容的一個(gè)臨時(shí)空間。在處理中,有時(shí)我們希望保留pattern space的內(nèi)容在下一次進(jìn)行處理,因此sed的開發(fā)者設(shè)計(jì)實(shí)現(xiàn)了hold space,并且提供了很多命令在pattern space和hold space之間進(jìn)行復(fù)制。記憶上,g和G都是get 的意思,表示從hold space取出放回pattern space,而h和H都是hold的意思,也就是從pattern space到hlod space。
基本語法:
g :將hold space中的內(nèi)容拷貝到pattern space中,原來pattern space里的內(nèi)容清除 G:pattern space末尾加上換行符后將hold space中的內(nèi)容append到pattern space中 h:將pattern space中的內(nèi)容拷貝到hold space中,原來的hold space里的內(nèi)容被清除 H:hold space末尾加上換行符后將pattern space中的內(nèi)容append到hold space中 x :交換 hold space 與 pattern space 內(nèi)容
Task13:倒置phonelist
我們可以拿一個(gè)簡(jiǎn)單的文件來理清思路:
A
B
C
D
如下圖:
除了第一行和最后一行處理不一樣以外(第一行只執(zhí)行h,而最后一行只執(zhí)行G),其余行都是用G、d和h(使用d 的原因是不把中間結(jié)果輸出)。在sed中有個(gè)操作是!,也就是編程語言中的“非”,即不執(zhí)行,因此,我們可以寫出sed命令來倒置一個(gè)文件:
JCHJCL01:/tmp/gnuhpc#sed '1!G;h;$!d' phonelist Llewellyn, Lee 817-8823 Jansen, Jan 903-3333 Llewellyn, Lynn 316-6221 Martin, Marty 814-5587 Christopher, Chris 305-5981 Robinson, Robin 411-3745 Patterson, Pat 614-6122 Franklin, Francis 704-3876 Terrell, Terry 617-7989
#p#
5.進(jìn)階語法篇之元字符的使用
sed有幾個(gè)很NB的元字符,這部分往往與正則表達(dá)式一起使用能夠得到事半功倍的效果。
基本語法:
& : 代表SELECTION中匹配的部分,常用于某個(gè)子字符串前后添加字符的操作
\num : num代表匹配子字符串的序號(hào),從1開始,\num表示匹配的子字符串(正則表達(dá)式中稱為分組),其中子字符串的匹配模式是由圓括號(hào)及其轉(zhuǎn)義字符構(gòu)成。
Task 15:將每個(gè)電話號(hào)碼前加上Tel:
JCHJCL01:/tmp/gnuhpc#sed '/[0-9]\{3\}-[0-9]\{4\}/s//Tel: &/g' phonelist Terrell, Terry Tel: 617-7989 Franklin, Francis Tel: 704-3876 Patterson, Pat Tel: 614-6122 Robinson, Robin Tel: 411-3745 Christopher, Chris Tel: 305-5981 Martin, Marty Tel: 814-5587 Llewellyn, Lynn Tel: 316-6221 Jansen, Jan Tel: 903-3333 Llewellyn, Lee Tel: 817-8823
可以看到前邊SELECTION是一個(gè)正則表達(dá)式來匹配電話號(hào)碼,也就是0-9三位數(shù)-0-9四位數(shù)這樣一個(gè)匹配邏輯,關(guān)鍵是元字符的使用,&代表了匹配的這串電話號(hào)碼,在前邊加上Tel:就是件很隨意的事情了。
Task16:電話號(hào)碼升級(jí),從原來的四位數(shù)統(tǒng)一升級(jí)為五位數(shù),6開頭。
JCHJCL01:/tmp/gnuhpc#sed 's/\([0-9]\{3\}\)-\([0-9]\{4\}\)/\1-6\2/g' phonelist Terrell, Terry 617-67989 Franklin, Francis 704-63876 Patterson, Pat 614-66122 Robinson, Robin 411-63745 Christopher, Chris 305-65981 Martin, Marty 814-65587 Llewellyn, Lynn 316-66221 Jansen, Jan 903-63333 Llewellyn, Lee 817-68823
Task17:將paths文件中的路徑用逗號(hào)連起來。首先,將每一行都放入hold space(以\n連接起來)而這個(gè)中間過程不顯示(也就是$!d所表示的除非處理到最后一行,否則都把pattern space刪掉),隨后在到最后一行時(shí)將hold space中的內(nèi)容放回pattern space(此處用了x,其實(shí)g也是可以的),并且把開頭的\n去掉后將剩余的\n替換為,最后打印。
JCHJCL01:/tmp/gnuhpc#sed 'H;$!d;${ > x > s/^\n// > s/\n/,/g > }' paths /opt/virtprovider/lib,/var/adm/syslog,/usr/bin/,/usr/local/bin
6.進(jìn)階語法篇之改變處理流程操作
有時(shí)我們希望對(duì)匹配的下一行或多行進(jìn)行操作,有時(shí)我們又希望在處理完畢后馬上退出(因?yàn)閟ed會(huì)對(duì)讀入文本的每一行進(jìn)行操作,即使肉眼看起來明顯不匹配也是需要sed先把字符串load進(jìn)來與SELECTION對(duì)照判斷的),此時(shí)就需要改變處理流程。
基本語法:
n:將之前讀入的行(也就是在pattern space中的行)輸出到屏幕,然后為將下一行的內(nèi)容提前讀入pattern space(替換上邊已經(jīng)打印的行),后續(xù)的命令會(huì)應(yīng)用到新讀入的行上。
q: 使用時(shí)前邊加行號(hào)n,表示取前n行,這個(gè)在讀取大文件的前幾行時(shí)有很大的作用。
N:將下一行的內(nèi)容讀取并追加到當(dāng)前模式空間中(用換行符作為連接),并沒有輸出當(dāng)前模式空間中的行。注意pattern space含有多行時(shí),正則表達(dá)式符號(hào)^和$含義分別為^匹配模式空間的最開始,而$是匹配模式空間的最后位置。
D:該命令刪除模式空間中從第一個(gè)字符到第一個(gè)換行符的內(nèi)容,并且跳轉(zhuǎn)到命令開頭重新執(zhí)行。注意,當(dāng)模式空間仍有內(nèi)容時(shí),不讀入新的輸入行,類似形成一個(gè)循環(huán)。
P:僅打印模式空間中從第一個(gè)字符到第一個(gè)換行符的內(nèi)容,重新在模式空間的內(nèi)容上執(zhí)行編輯命令,類似形成一個(gè)循環(huán)。
:label和b label : 標(biāo)注一個(gè)標(biāo)簽并跳轉(zhuǎn)。例如,下面的例子中模擬了一個(gè)if操作存在符合pattern則跳過command2直接執(zhí)行command3
command1 /pattern/b goto command2 :goto command3
而下邊的例子則模擬了一個(gè)if else操作,符合pattern時(shí)執(zhí)行command3,不符合時(shí)執(zhí)行command3
command1 /pattern/b dosomething command2 b :dosomething command3
Task18:將QMPath后邊的空行刪掉
JCHJCL01:/tmp/gnuhpc#sed '/QMPath/{n > /^$/d > }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
Task19: 取一個(gè)大文件的前兩行,可以看到同樣都是取前兩行,由于file這個(gè)文件較大,最終導(dǎo)致效率的差別是幾十倍。
JCHJCL01:/tmp/gnuhpc#ls -l file -rw-r--r-- 1 root system 78888888 Jan 25 23:20 file JCHJCL01:/tmp/gnuhpc#time sed -n '1,2p' file 1 2 real 0m0.78s user 0m0.35s sys 0m0.13s JCHJCL01:/tmp/gnuhpc#time sed '2q' file 1 2 real 0m0.01s user 0m0.00s sys 0m0.00s
Task20:將多個(gè)連續(xù)空行縮減為一個(gè)空行,非連續(xù)空行保留。$q表示最后一行不進(jìn)行處理,因?yàn)橛捎谇斑叺奶幚?,到了最后一行已?jīng)不會(huì)出現(xiàn)連續(xù)空行了。其余的處理邏輯為:匹配空行,讀入下一行,發(fā)現(xiàn)下一行還是空行后刪除第一個(gè)空行然后繼續(xù)讀、處理直到下一行不是空行為止。
JCHJCL01:/tmp/gnuhpc# sed '/^$/{$q N /^\n$/D }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
#p#
6.sed技巧拾零篇
本來想介紹一下正則表達(dá),無奈太博大精深,幾個(gè)例子也說明不了太多,因此正則表達(dá)部分可以參考sed手冊(cè),里面有比較詳盡的講解。此處舉一些常用和手冊(cè)上沒有提及的用例。
[:alnum:]:表示所有的字母和數(shù)字
[:digit:]: 表示所有數(shù)字
[:upper:]: 表示所有的大寫字母
[:lower:] :表示所有的小寫字母
Task21:去掉config.ini中的數(shù)字,使得config.ini變?yōu)橐粋€(gè)配置文件模板
JCHJCL01:/tmp/gnuhpc#sed 's/[[:digit:]]//g' config.ini [WQMInfo] ProxyMode= Proxy= RunMode= LastStatisticTime=-- RecordMode= RecordKeyBoard= RecordMouseClick= RecordMouseMove= QMPath= [BROWSE_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide= [DEVELOP_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide= [RUN_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide=
利用SELECTION進(jìn)行取符合匹配條件的連續(xù)多行:
Task22:取得config.ini中WQMInfo段
JCHJCL01:/tmp/gnuhpc#sed -n '/WQMInfo/,/^$/p' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath=
取匹配條件的上N行和下N行:
Task23:取得通訊錄中Martin上邊的一個(gè)人的記錄,讀入下一行到模式空間,并且判斷是否包含Lynn,如果匹配,則打印模式空間中的第一行,如果不匹配,則刪除模式空間的第一行,循環(huán)處理。
JCHJCL01:/tmp/gnuhpc#sed -n '$!N;/Lynn/!D;/Lynn/P' phonelist Martin, Marty 814-5587
Task24:取得Martin上邊包含Martin的所有人記錄:
JCHJCL01:/tmp/gnuhpc#sed -n '1,/Martin/p' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587
Task25:取得Martin上邊包含Martin的三條記錄:
JCHJCL01:/tmp/gnuhpc#sed -n '1,/Martin/p' phonelist | tail -3 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587
Task26:取得通訊錄中Martin下邊的一個(gè)人的記錄:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/{n > p > }' phonelist Llewellyn, Lynn 316-6221
Task27:取得Martin下邊包含Martin的所有人記錄:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/,$p' phonelist Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task28:取得Martin下邊包含Martin的三條記錄:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/,$p' phonelist | head -3 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333
注釋掉某些行:
Task29:假設(shè)ini文件行注釋為前后兩個(gè)感嘆號(hào),請(qǐng)注釋掉RecordMouseMove
JCHJCL01:/tmp/gnuhpc#sed 's/^RecordMouseMove.*/!!&!!/' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 !!RecordMouseMove=0!! QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1
Task30:假設(shè)paths文件用#進(jìn)行注釋,則注釋掉含有usr的行
JCHJCL01:/tmp/gnuhpc#sed 's/.*usr.*/#&/' paths /opt/virtprovider/lib /var/adm/syslog #/usr/bin/ #/usr/local/bin
一個(gè)更簡(jiǎn)單的方法是:
JCHJCL01:/tmp/gnuhpc#sed '/usr/s/^/#/' paths /opt/virtprovider/lib /var/adm/syslog #/usr/bin/ #/usr/local/bin
Task31:在paths中每行加一個(gè)行號(hào)和冒號(hào)
JCHJCL01:/tmp/gnuhpc#sed = paths | sed 'N;s/\n/:/' 1:/opt/virtprovider/lib 2:/var/adm/syslog 3:/usr/bin/ 4:/usr/local/bin
Task32: 處理XML
JCHJCL01:/tmp/gnuhpc#echo "<Amount>10kg</Amount>" | sed 's#\(<Amount>\)[0-9,a-z]*\(</Amount>\)#\1'100kg'\2#g'
<Amount>100kg</Amount>
Task33:替代一行中多個(gè)匹配模式中的其中一個(gè)。對(duì)于下邊的解釋:前者替換倒數(shù)第二個(gè)匹配;后者替換最后一個(gè)匹配 。
sed 's/(.*)foo(.*foo)/1bar2/ test.txt sed 's/(.*)foo/1bar/' test.txt
Task34:刪除paths的最后2行,在讀入第一行后,使用N讀入一行,并且如果新讀入的下一行不是最后一行,則打印模式空間中的第一行,并且刪除,然后接著執(zhí)行N;如果新讀入的一行是文件的最后一行,則刪除模式空間中的所有內(nèi)容(此時(shí)pattern space中即為倒數(shù)兩行)。
JCHJCL01:/tmp/gnuhpc#sed 'N;$!P;$!D;$d' paths /opt/virtprovider/lib /var/adm/syslog
小結(jié)篇
本文已幾個(gè)文本文件為例子,說明了sed的基本用法指南,由于我本人傾向于腳本要具有高度的可移植性,另外同一個(gè)任務(wù)不一定都要交給一個(gè)工具完成,多個(gè)工具配合使用,在不太考慮性能的前提下,simpler better,因此諸多高級(jí)用法和GNU sed的用法均在此文沒有涉及。