Sed and awk 筆記之 sed 篇:基礎命令

sed篇總共分成6章:(簡書版)

Sed&awk筆記之sed篇:簡單介紹

Sed&awk筆記之sed篇:模式空間與地址匹配

Sed&awk筆記之sed篇:sed基礎命令

Sed&awk筆記之sed篇:高級命令(一)

Sed&awk筆記之sed篇:高級命令(二)

Sed&awk筆記之sed篇:實戰

在開始之前,首先回顧上一篇的重點內容:地址匹配。上一篇中介紹過,地址可以指定0個,1個或者2個。地址的形式可以為斜杠分隔的正則表達式(例如/test/),行號(例如3,5)或者特殊符號(例如$)。如果沒有指定地址,說明sed應用的編輯命令是全局的;如果是1個地址,編輯命令只是應用到匹配的那一行;如果是一對地址,編輯命令則應用到該地址對匹配的行范圍。關于地址匹配的內容具體可以看Sed命令地址匹配問題總結

書中說,對于sed編輯命令的語法有兩種約定,分別是

[address]command? ? ? ? ? ? ? # 第一種

[line-address]command? ? ? ? ? # 第二種

第一種[address]是指可以為任意地址包括地址對,第二種[line-address]是指只能為單個地址。文中指出,例如i(后方插入命令)、a(前方追加命令)以及=(打印行號命令)等都屬于第二種命令,但是實際上我在ArchLinux上測試,指定地址對也沒有報錯。不過為了兼容性,建議按作者所說的做。

以下是要介紹的全部基礎命令:

名稱命令語法說明

替換s[address]s/pattern/replacement/flags替換匹配的內容

刪除d[address]d刪除匹配的行

插入i[line-address]i\

text在匹配行的前方插入文本

追加a[line-address]a\

text在匹配行的后方插入文本

行替換c[address]c\

text將匹配的行替換成文本text

打印行p[address]p打印在模式空間中的行

打印行號=[address]=打印當前行行號

打印行l[address]l打印在模式空間中的行,同時顯示控制字符

轉換字符y[address]y/SET1/SET2/將SET1中出現的字符替換成SET2中對應位置的字符

讀取下一行n[address]n將下一行的內容讀取到模式空間

讀文件r[line-address]r file將指定的文件讀取到匹配行之后

寫文件w[address]w file將匹配地址的所有行輸出到指定的文件中

退出q[line-address]q讀取到匹配的行之后即退出

替換命令: s

替換命令的語法是:

[address]s/pattern/replacement/flags

其中[address]是指地址,pattern是替換命令的匹配表達式,replacement則是對應的替換內容,flags是指替換的標志位,它可以包含以下一個或者多個值:

n: 一個數字(取值范圍1-512),表明僅替換前n個被pattern匹配的內容;

g: 表示全局替換,替換所有被pattern匹配的內容;

p: 僅當行被pattern匹配時,打印模式空間的內容;

w file:僅當行被pattern匹配時,將模式空間的內容輸出到文件file中;

如果flags為空,則默認替換第一次匹配:

$ echo"column1 column2 column3 column4"|sed's/ /;/'column1;column2 column3 column4

如果flags中包含g,則表示全局匹配:

$ echo"column1 column2 column3 column4"|sed's/ /;/g'column1;column2;column3;column4

如果flags中明確指定替換第n次的匹配,例如n=2:

$ echo"column1 column2 column3 column4"|sed's/ /;/2'column1 column2;column3 column4

當替換命令的pattern與地址部分是一樣的時候,比如/regexp/s/regexp/replacement/可以省略替換命令中的pattern部分,這在單個編輯命令的情況下沒多大用處,但是在組合命令的場景下還是能省不少功夫的,例如下面是摘自文中的一個段落:

The substitute command is applied to the lines matching the address. If no

address is specified, it is applied to all lines that match the pattern, a

regular expression. If a regular expression is supplied as an address, and no

pattern is specified, the substitute command matches what is matched by the

address.? This can be useful when the substitute command is one of multiple

commands applied at the same address. For an example, see the section "Checking

Out Reference Pages" later in this chapter.

現在要在substitute command后面增加("s"),同時在被修改的行前面增加+號,以下是使用的sed命令:

$ sed'/substitute command/{s//&("s")/;s/^/+ /}'paragraph.txt

這里我們用到了組合命令,并且地址匹配的部分和第一個替換命令的匹配部分是一樣的,所以后者我們省略了,在replacement部分用到了&這個元字符,它代表之前匹配的內容,這點我們在后面介紹。執行后的結果為:

+ The substitute command("s") is applied to the lines matching the address. If no

address is specified, it is applied to all lines that match the pattern, a

regular expression. If a regular expression is supplied as an address, and no

+ pattern is specified, the substitute command("s") matches what is matched by the

+ address.? This can be useful when the substitute command("s") is one of multiple

commands applied at the same address. For an example, see the section "Checking

Out Reference Pages" later in this chapter.

替換命令的一個技巧是中間的分隔符是可以更改的(這個技巧我們在簡潔的Bash編程技巧續篇 - 5. 你知道sed的這個特性嗎?中也曾經介紹過),這個技巧在有些地方非常有用,比如路徑替換,下面是采用默認的分隔符和使用感嘆號作為分隔符的對比:

$ find/usr/local-maxdepth2-type d|sed's/\/usr\/local\/man/\/usr\/share\/man/'$ find/usr/local-maxdepth2-type d|sed's!/usr/local/man!/usr/share/man!'

替換命令中還有一個很重要的部分——replacement(替換內容),即將匹配的部分替換成replacement。在replacemnt部分中也有幾個特殊的元字符,它們分別是:

&: 被pattern匹配的內容;

\num: 被pattern匹配的第num個分組(正則表達式中的概念,\(..\)括起來的部分稱為分組;

\: 轉義符號,用來轉義&,\, 回車等符號

&元字符我們已經在上面的例子中介紹過,這里舉個例子介紹下\num的用法。在Linux系統中,默認都開了幾個tty可以讓你切換(ctrl+alt+1, ctrl+alt+2 ...),例如CentOS 6.0+的系統默認是6個:

[root@localhost~]#grep-B1ACTIVE_CONSOLES/etc/sysconfig/init# What ttys should gettys be started on?ACTIVE_CONSOLES=/dev/tty[1-6]

可以通過修改上面的配置來減少開機啟動的時候創建的tty個數,比如我們只想要2個:

[root@localhost~]#sed-r-i's!(ACTIVE_CONSOLES=/dev/tty\[1-)6]!\12]!'/etc/sysconfig/init

ACTIVE_CONSOLES=/dev/tty[1-2]

其中-i參數表示直接修改原文件,-r參數是指使用擴展的正則表達式(ERE),擴展的正則表達式中分組的括號不需要用反斜杠轉義"(ACTIVE_CONSOLES=/dev/tty\[1-)",這里[是有特殊含義的(表示字符組),所以需要轉義。在替換的內容中使用\1來引用這個匹配的分組內容,1代表分組的編號,表示第一個分組。

刪除命令: d

刪除命令的語法是:

[address]d

刪除命令可以用于刪除多行內容,例如1,3d會刪除1到3行。刪除命令會將模式空間中的內容全部刪除,并且導致后續命令不會執行并且讀入新行,因為當前模式空間的內容已經為空。我們可以試試:

$ sed'2,${d;=}'listJohnDaggett,341KingRoad,PlymouthMA

以上命令嘗試在刪除第2行到最后一行之后,打印當前行號(=),但是事實上并沒有執行該命令。

插入行/追加行/替換行命令: i/a/c

這三個命令的語法如下所示:

# Append 追加

[line-address]a\

text

# Insert 插入

line-address]i\

text

# Change 行替換

[address]c\

text

以上三個命令,行替換命令(c)允許地址為多個地址,其余兩個都只允許單個地址(注:在ArchLinux上測試表明,追加和插入命令都允許多個地址,sed版本為GNU sed version 4.2.1

追加命令是指在匹配的行后面插入文本text;相反地,插入命令是指匹配的行前面插入文本text;最后,行替換命令會將匹配的行替換成文本text。文本text并沒有被添加到模式空間,而是直接輸出到屏幕,因此后續的命令也不會應用到添加的文本上。注意,即使使用-n參數也無法抑制添加的文本的輸出。

我們用實際的例子來簡單介紹下這三個命令的用法,依然用最初的文本list:

$ cat listJohnDaggett,341KingRoad,PlymouthMAAliceFord,22EastBroadway,RichmondVAOrvilleThomas,11345OakBridgeRoad,TulsaOKTerryKalkas,402LansRoad,BeaverFallsPAEricAdams,20PostRoad,SudburyMAHubertSims,328ABrookRoad,RoanokeVAAmyWilde,334BayshorePkwy,MountainViewCASalCarpenter,736thStreet,BostonMA

現在,我們要在第2行后面添加'------':

$ sed'2a\

--------------------------------------

'listJohnDaggett,341KingRoad,PlymouthMAAliceFord,22EastBroadway,RichmondVA--------------------------------------OrvilleThomas,11345OakBridgeRoad,TulsaOKTerryKalkas,402LansRoad,BeaverFallsPAEricAdams,20PostRoad,SudburyMAHubertSims,328ABrookRoad,RoanokeVAAmyWilde,334BayshorePkwy,MountainViewCASalCarpenter,736thStreet,BostonMA

或者可以在第3行之前插入:

$ sed'3i\

--------------------------------------

'listJohnDaggett,341KingRoad,PlymouthMAAliceFord,22EastBroadway,RichmondVA--------------------------------------OrvilleThomas,11345OakBridgeRoad,TulsaOKTerryKalkas,402LansRoad,BeaverFallsPAEricAdams,20PostRoad,SudburyMAHubertSims,328ABrookRoad,RoanokeVAAmyWilde,334BayshorePkwy,MountainViewCASalCarpenter,736thStreet,BostonMA

我們來測試下文本是否確實沒有添加到模式空間,因為模式空間中的內容默認是會打印到屏幕的:

$ sed-n'2a\

--------------------------------------

'list--------------------------------------

通過-n參數來抑制輸出后發現插入的內容依然被輸出,所以可以判定插入的內容沒有被添加到模式空間。

使用行替換命令將第2行到最后一行的內容全部替換成'----':

$ sed'2,$c\

--------------------------------------

'listJohnDaggett,341KingRoad,PlymouthMA--------------------------------------

打印命令: p/l/=

這里純粹的打印命令應該是指p,但是因為后兩者(l和=)和p差不多,并且相對都比較簡單,所以這里放到一起介紹。

這三個命令的語法是:

[address]p

[address]=

[address]l

p命令用于打印模式空間的內容,例如打印list文件的第一行:

$ sed-n'1p'listJohnDaggett,341KingRoad,PlymouthMA

l命令類似p命令,不過會顯示控制字符,這個命令和vim的list命令相似,例如:

$ echo"column1 column2 column3^M"|sed-n'l'column1\tcolumn2\tcolumn3\r$

=命令顯示當前行行號,例如:

$ sed'='list1JohnDaggett,341KingRoad,PlymouthMA2AliceFord,22EastBroadway,RichmondVA3OrvilleThomas,11345OakBridgeRoad,TulsaOK4TerryKalkas,402LansRoad,BeaverFallsPA5EricAdams,20PostRoad,SudburyMA6HubertSims,328ABrookRoad,RoanokeVA7AmyWilde,334BayshorePkwy,MountainViewCA8SalCarpenter,736thStreet,BostonMA

轉換命令: y

轉換命令的語法是:

[address]y/SET1/SET2/

它的作用是在匹配的行上,將SET1中出現的字符替換成SET2中對應位置的字符,例如1,3y/abc/xyz/會將1到3行中出現的a替換成x,b替換成y,c替換成z。是不是覺得這個功能很熟悉,其實這一點和tr命令是一樣的。可以通過y命令將小寫字符替換成大寫字符,不過命令比較長:

$ echo"hello, world"|sed'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'HELLO,WORLD

使用tr命令來轉換成大寫:

$ echo"hello, world"|tr a-z A-Z

HELLO,WORLD

取下一行命令: n

取下一行命令的語法為:

[address]n

n命令為將下一行的內容提前讀入,并且將之前讀入的行(在模式空間中的行)輸出到屏幕,然后后續的命令會應用到新讀入的行上。因此n命令也會同d命令一樣改變sed的控制流程。

書中給出了一個例子來介紹n的用法,假設有這么一個文本:

$ cat text.H1"On Egypt"Napoleon,pointing to thePyramids,said to his troops:"Soldiers, forty centuries have their eyes upon you."

現在要將.H1后面的空行刪除:

$ sed'/.H1/{n;/^$/d}'text.H1"On Egypt"Napoleon,pointing to thePyramids,said to his troops:"Soldiers, forty centuries have their eyes upon you."

讀寫文件命令: r/w

讀寫文件命令的語法是:

[line-address]r file

[address]w file

讀命令將指定的文件讀取到匹配行之后,并且輸出到屏幕,這點類似追加命令(a)。我們以書中的例子來講解讀文件命令。假設有一個文件text:

$ cat textForservice,contact any of the following companies:[Company-list]Thankyou.

同時我們有一個包含公司名稱列表的文件company.list:

$ cat company.listAlliedMayflowerUnited

現在我們要將company.list的內容讀取到[Company-list]之后:

$ sed'/^\[Company-list]/r company.list'textForservice,contact any of the following companies:[Company-list]AlliedMayflowerUnitedThankyou.

更好的處理應當是用公司名稱命令替換[Company-list],因此我們還需要刪除[Company-list]這一行:

$ sed'/^\[Company-list]/r company.list;/^\[Company-list]/d;'textForservice,contact any of the following companies:[Company-list]Thankyou.

但是結果我們非但沒有刪除[Company-list],而且company.list的內容也不見了?這是怎么回事呢?

下面是我猜測的過程,讀文件的命令會將r空格后面的所有內容都當成文件名,即company.list;/^\[Company-list]/d;,然后讀取命令的時候發現該文件不存在,但是sed命令讀取不存在的文件是不會報錯的。所以什么事都沒干成。

我們用-e選項將兩個命令分開就正常了:

$ sed-e'/^\[Company-list]/r company.list'-e'/^\[Company-list]/d;'textForservice,contact any of the following companies:AlliedMayflowerUnitedThankyou.

寫命令將匹配地址的所有行輸出到指定的文件中。假設有一個文件內容如下,前半部分是人名,后半部分是區域名稱:

$ cat textAdams,HenriettaNortheastBanks,FredaSouthDennis,JimMidwestGarvey,BillNortheastJeffries,JaneWestMadison,SylviaMidwestSommes,TomSouth

現在我們要將不同區域的人名字寫到不同的文件中:

$ sed'/Northeast$/w region.northeast

> /South$/w region.south

> /Midwest$/w region.midwest

> /West$/w region.west'textAdams,HenriettaNortheastBanks,FredaSouthDennis,JimMidwestGarvey,BillNortheastJeffries,JaneWestMadison,SylviaMidwestSommes,TomSouth

查看輸出的文件:

$ cat region.region.midwest? ? region.northeast? region.south? ? ? region.west

$ cat region.midwestDennis,JimMidwestMadison,SylviaMidwest

我們也可以試試將所有的w命令放到一起用分號分隔,發現會出下面的錯誤,它把w空格后面的部分當作文件名,這樣就驗證了上面的猜測:

$ sed'/Northeast$/w region.northeast;/south$/w region.south;'text

sed:couldn't open file region.northeast;/south$/w region.south;: No such file or directory

退出命令: q

退出命令的語法是

[line-address]q

當sed讀取到匹配的行之后即退出,不會再讀入新的行,并且將當前模式空間的內容輸出到屏幕。例如打印前3行內容:

$ sed'3q'listJohnDaggett,341KingRoad,PlymouthMAAliceFord,22EastBroadway,RichmondVAOrvilleThomas,11345OakBridgeRoad,TulsaOK

打印前3行也可以用p命令:

$ sed-n'3p'listJohnDaggett,341KingRoad,PlymouthMAAliceFord,22EastBroadway,RichmondVAOrvilleThomas,11345OakBridgeRoad,TulsaOK

但是對于大文件來說,前者比后者效率更高,因為前者讀取到第N行之后就退出了。后者雖然打印了前N行,但是后續的行還是要繼續讀入,只不會不作處理。

到此為止,sed基礎命令的部分就介紹完了。下面一篇的內容會在最近幾天更新上來,主要會介紹sed高級命令。不過一般情況下,會使用基礎命令已經差不多了,高級命令不一定需要去了解。

下面是我整理的一份思維導圖(附.xmind文件下載地址):

轉自:團子的小窩, 本文固定鏈接:Sed and awk 筆記之 sed 篇:基礎命令

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容