sed原理及使用

前言

環境:centos6.5html

sed版本:GNU sed version 4.2.1正則表達式

本文的代碼都是在這個環境下驗證的。shell

1、簡介

sed(Stream Editor)意爲流編輯器,是Unix常見的命令行程序。是Bell實驗室的 Lee E.McMahon 在1973年到1974年之間開發完成,目前能夠在大多數操做系統中使用。編程

sed的出現是做爲grep的一個繼任者,由於grep只能簡單的進行查找和替換,可是考慮還可能會有刪除等各類需求,McMahon 開發了一個更具通用性的工具。sed著名的語法規則包括使用 / 進行模式匹配,以及 s/// 來進行替代。與同期存在的工具ed一塊兒,sed的語法影響了後來發展的 ECMAScript 和 Perl。GNU sed 添加了不少特性,包括著名的 in-place editing。vim

 

2、處理流程

要說明sed的處理流程,須要先提它的命令語法:centos

sed [options] script filename

通常來講,sed是從stdin讀取輸入,而且將輸出寫出到stdout,可是filename被指定時,會從指定的文件中獲取輸入,輸出能夠重定向到另外的文件中。緩存

options指的是sed的命令行參數,比較有限,這個後面會說明。app

script是指須要對輸入執行的一個或者多個操做指令,通常須要用單引號括起來,這樣能夠避免shell對特殊字符的處理。sed會依次讀取輸入文件的每一行到緩存中並應用script中指定的操做指令,所以而帶來的變化並不會影響最初的文件(除非option加了-i參數)。編程語言

若是操做指令不少,爲了避免影響可讀性,能夠將其寫入文件,並用-f參數指定該文件:編輯器

sed -f scriptfile filename

不管是將操做指令經過命令行指定,仍是寫入到文件中做爲一個sed腳本,必須包含至少一個指令,不然用sed就沒有意義了。

每條操做指令由patternprocedure兩部分組成,顧名思義,pattern是匹配的規則,通常爲用'/'分隔的正則表達式(也有多是行號,具體參見Sed命令地址匹配問題總結),而procedure則是一連串編輯命令(action)。 sed的處理流程,簡化後是這樣的:

  1. 讀入新的一行內容到緩存空間;
  2. 從指定的操做指令中取出第一條指令,判斷是否匹配pattern;
  3. 若是不匹配,則忽略後續的編輯命令,回到第2步繼續取出下一條指令;
  4. 若是匹配,則針對緩存的行執行後續的編輯命令;完成後,回到第2步繼續取出下一條指令;
  5. 當全部指令都應用以後,輸出緩存行的內容;回到第1步繼續讀入下一行內容;
  6. 當全部行都處理完以後,結束;

具體流程如圖所示:

simple_sed_process_flow_chart

 

3、命令選項options

根據上面提到的,咱們知道sed通常有兩種語法形式:

sed [options] script filename
sed -f scriptfile filename

 

options經常使用的只有幾個:

-n :使用安靜(silent)模式。在通常 sed 的用法中,全部來自 STDIN 的數據通常都會被列出到終端上。但若是加上 -n 參數後,則只有通過sed 特殊處理的那一行(或者動做)纔會被列出來。

-e :直接在命令列模式上進行 sed 的動做編輯;

-f :直接將 sed 的動做寫在一個文件內, -f filename 則能夠運行 filename 內的 sed 動做;

-r :sed 的動做支持的是延伸型正規表示法的語法。(默認是基礎正規表示法語法)

-i :直接修改讀取的文件內容,而不是輸出到終端。

 

在這裏,-e參數實際上不少時候會讓人很迷惑。由於咱們平時使用sed的時候通常都不會加-e參數,譬如:

seq 6 | sed -n '1,2p'

它和下面的寫法是等價的:

seq 6 | sed -ne '1,2p'

因此不少文章在說到-e這個參數的時候說能夠省略,實際上,這種說法是不嚴謹的。sed發展到如今,已經有各類各樣的版本,在有些版本中,-e參數是不能夠省略的,可是咱們通常使用的都是GNU sed(gsed),因此在這種狀況能夠省略。-e參數最經常使用的場景是對同一文件做屢次修改,如:

seq 6 | sed -e 's/1/10/' -e 's/2/20/'

可是實際上多重編輯還有更簡潔的寫法,就是在同一個script中,用分號分割兩句指令:

seq 6 | sed -e 's/1/10/;s/2/20/'

 

在處理流程部分,咱們提到,每條操做指令script通常由patternprocedure兩部分組成,接下來講說pattern和procedure。

 

4、pattern

1. 模式空間

回顧一下上面講到的處理流程,咱們能夠知道,sed是以行的方式來處理數據的,每一行被讀入到一塊緩存空間,該空間名爲模式空間(pattern space)。所以sed操做的都是最初行的拷貝,同時後續的編輯命令都是應用到前面的命令編輯後輸出的結果,因此編輯命令之間的順序就顯得格外重要。

讓咱們來看一個很是簡單的例子,將一段文本中的pig替換成cow,而且將cow替換成horse:

echo "pig and cow and horse" | sed 's/pig/cow/;s/cow/hores/'

初看起來好像沒有問題,可是其實是錯誤的,最後輸出其實是:

hores and cow and horse

緣由是第一個替換命令將全部的pig都替換成cow,緊接着的替換命令是基於前一個結果處理的,將全部的cow都替換成horse,形成的結果是所有的pig/cow都被替換成了horse,這樣違背了咱們的初衷。在這種狀況下,只須要調換下兩個編輯命令的順序:

echo "pig and cow and horse" | sed 's/cow/hores/;s/pig/cow/'

 

2. 模式空間的轉換

sed只會緩存一行的內容在模式空間,這樣的好處是sed能夠處理大文件而不會有任何問題,不像一些編輯器由於要一次性載入文件的一大塊內容到緩存中而致使內存不足。

例如,如今要把一段文本中的Unix System與UNIX System都要統一替換成The UNIX Operating System,所以咱們用兩句替換命令來完成這個目的:

echo "the Unix System" | sed 's/Unix/UNIX/;s/UNIX System/UNIX Operating System/'
==> the UNIX Operating System

過程簡單來講以下:

1)讀入一行內容到模式空間

2)應用第一條替換命令將Unix換成UNIX

3)模式空間的內容變爲」the UNIX System」

4)應用第二條替換命令將UNIX System替換成UNIX Operating System

5)模式空間的內容變爲」the UNIX Operating System」

6)全部編輯命令執行完畢,輸出模式空間中的行

 

3. 地址匹配

地址匹配限制sed的編輯命令到底應用在哪些行上。默認狀況下,sed是全局匹配的,即對全部輸入行都應用指定的編輯命令,這是由於sed依次讀入每一行,每一行都會成爲當前行並被處理,因此s/apple/Pear/g會將全部輸入行的apple替換成pear。

但若是經過指定地址範圍,則只會替換範圍內的行,譬如:

echo "Harry loves apple" | sed '/Mary/s/apple/pear/'
==> Harry loves apple

/Mary/是一個正則表達式,表示匹配包含Mary的行,所以」Harry loves apple」不會被替換.

sed命令中能夠包含0個、1個或者2個地址(地址對)。地址能夠爲正則表達式(如/Mary/),行號或者特殊的行符號(如$表示最後一行):

  • 若是沒有指定地址,默認將編輯命令應用到全部行
  • 若是指定一個地址,只將編輯命令應用到匹配該地址的行;
  • 若是指定一個地址對(addr1,addr2),則將編輯命令應用到地址對中的全部行(包括起始和結束);
  • 若是地址後面有一個感嘆號(!),則將編輯命令應用到不匹配該地址的全部行

爲了方便理解上述內容,咱們以刪除命令(d)爲例來講明。

1)以行號指定地址

默認不指定地址將會刪除全部行:

sed 'd' file

指定地址則刪除匹配的行,如刪除第一行:

sed '1d' file

或者刪除最後一行,$符號在這裏表示最後一行,這點要下正則表達式中的含義區別開來:

sed '$d' file

經過指定地址對能夠刪除該範圍內的全部行,例如刪除第3行到最後一行:

sed '2,$d' file

這裏經過指定行號刪除,行號是sed命令內部維護的一個計數變量,該變量只有一個,而且在多個文件輸入的狀況下也不會被重置。

 

2)經過正則表達式來指定地址

刪除包含MA的行:

sed '/MA/d' file

刪除從包含Alice的行開始到包含Hubert的行結束的全部行:

sed '/Alice/,/Hubert/d' file

固然,行號和地址對是能夠混用的,刪除第二行到包含Hubert的行結束的全部行:

sed '2,/Hubert/d' file

若是在地址後面指定感嘆號(!),則會將命令應用到不匹配該地址的行:

sed '2,/Hubert/!d' file

以上介紹的都是最基本的地址匹配形式,GNU Sed基於此添加了幾個擴展的形式,具體能夠看man手冊,或者能夠看Sed 命令地址匹配問題總結

上面說的內容都是對匹配的地址執行單個命令,若是要執行多個編輯命令要怎麼辦?sed中能夠用{}來組合命令,就比如編程語言中的語句塊,例如:

sed -n '1,4{s/ MA/, Massachusetts/;s/ PA/, Pennsylvania/;p}' file

 

5、procedure

對於sed編輯命令的語法有兩種約定,分別是

[address]procedure               # 第一種
[line-address]procedure          # 第二種

第一種[address]是指能夠爲任意地址包括地址對,第二種[line-address]是指只能爲單個地址。

如下是要介紹的所有基礎命令:

名稱

命令

語法

說明

替換

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

讀取到匹配的行以後即退出

 

1. 替換命令: 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部分,這在單個編輯命令的狀況下沒多大用處,可是在組合命令的場景下仍是能省很多功夫的。

 

譬如咱們有一個文件prince,裏面的內容是:

If someone loves a flower, of which just one single blossom grows in all the 
millions and millions of stars, it is enough to make him happy just to look at 
the stars. He can say to himself, "Somewhere, my flower is there…" But if the 
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important

!

如今要在flower後面增長(「s」),同時在被修改的行前面增長+號,如下是使用的sed命令:

sed '/flower/{s//&("s")/;s/^/+ /}' prince

這裏咱們用到了組合命令,而且地址匹配的部分和第一個替換命令的匹配部分是同樣的,因此後者咱們省略了,在replacement部分用到了&這個元字符,它表明以前匹配的內容,這點咱們在後面介紹。執行後的結果爲:

+

 If someone loves a flower

("s"),

 of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at 

+

 the stars. He can say to himself, "Somewhere, my flower

("s")

 is there…" But if the 

+

 sheep eats the flower

("s")

, in one moment all his stars will be darkened… And you think that is not important!
替換命令的一個技巧是中間的分隔符是能夠更改的,這個技巧在有些地方很是有用,好比路徑替換,下面是採用默認的分隔符和使用感嘆號做爲分隔符的對比:
find /usr/local -maxdepth 2 -type d | sed 's//usr/local/man//usr/share/man/'
find /usr/local -maxdepth 2 -type d | sed 's!/usr/local/man!/usr/share/man!'

 

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

  • &: 被pattern匹配的內容;
  • \num: 被pattern匹配的第num個分組(正則表達式中的概念,\(..\)括起來的部分稱爲分組;
  • \: 轉義符號,用來轉義&,\, 回車等符號

2. 刪除命令: d

語法:

[address]d

刪除命令能夠用於刪除多行內容,例如1,3d會刪除1到3行。刪除命令會將模式空間中的內容所有刪除,而且致使後續命令不會執行而且讀入新行,由於當前模式空間的內容已經爲空。咱們能夠試試:

sed '2,${d;=}' prince 
==> If someone loves a flower, of which just one single blossom grows in all the

以上命令嘗試在刪除第2行到最後一行以後,打印當前行號(=),可是事實上並無執行該命令。

 

3. 插入行/追加行/替換行命令: 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參數也沒法抑制添加的文本的輸出。

咱們用實際的例子來簡單介紹下這三個命令的用法,用上面的文本prince:

如今,咱們要在第2行後面添加'------':

sed '2a\
-------------------------\
' prince

輸出:

If someone loves a flower, of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at
-------------------------

the stars. He can say to himself, "Somewhere, my flower is there…" But if the
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!

或者能夠在第3行以前插入:

sed '3i\
--------------------------\
 ' prince

輸出:

If someone loves a flower, of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at
--------------------------

the stars. He can say to himself, "Somewhere, my flower is there…" But if the
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!

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

sed -n '2a\
---------------------------\
' prince

輸出:

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

經過-n參數來抑制輸出後發現插入的內容依然被輸出,因此能夠斷定插入的內容沒有被添加到模式空間。

使用行替換命令將第2行到最後一行的內容所有替換成'----':

sed '2,$c\
---------------------------\
' prince

輸出:

If someone loves a flower, of which just one single blossom grows in all the
---------------------------

 

4. 打印命令: p/l/=

這裏純粹的打印命令應該是指p,可是由於後二者(l和=)和p差很少,而且相對都比較簡單,因此這裏放到一塊兒介紹。

[address]p
[address]=
[address]l

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

sed -n '1p' prince
==> If someone loves a flower, of which just one single blossom grows in all the

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

echo "column1 column2 column3^M" | sed -n 'l'
==> column1tcolumn2tcolumn3r$

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

sed '=' prince
==>
1
If someone loves a flower, of which just one single blossom grows in all the 
2
millions and millions of stars, it is enough to make him happy just to look at 
3
the stars. He can say to himself, "Somewhere, my flower is there…" But if the 
4
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!
5

 

5. 轉換命令: 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

 

6. 取下一行命令: n

語法:

[address]n

n命令爲將下一行的內容提早讀入,而且將以前讀入的行(在模式空間中的行)輸出到屏幕,而後後續的命令會應用到新讀入的行上。所以n命令也會同d命令同樣改變sed的控制流程。

cat text

==>

.H1 "On Egypt"

Napoleon, pointing to the Pyramids, said to his troops:

"Soldiers, forty centuries have their eyes upon you."

如今要將.H1後面的空行刪除:

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

 

7.  讀寫文件命令: r/w

語法是:

[line-address]r file
[address]w file

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

cat text
==> 
For service, contact any of the following companies:
[Company-list]
Thank you.

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

cat company.list 
==>
Allied
Mayflower
United

如今咱們要將company.list的內容讀取到[Company-list]以後:

sed '/^[Company-list]/r company.list' text
=>
For service, contact any of the following companies:
[Company-list]
Allied
Mayflower
United
Thank you.

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

cat text 
==>
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South

如今咱們要將不一樣區域的人名字寫到不一樣的文件中:

sed '/Northeast$/w region.northeast
/South$/w region.south
/Midwest$/w region.midwest
/West$/w region.west' text
==>
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South

 

8. 退出命令: q

語法:

[line-address]q

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

sed '3q' prince
==>
If someone loves a flower, of which just one single blossom grows in all the 
millions and millions of stars, it is enough to make him happy just to look at 
the stars. He can say to himself, "Somewhere, my flower is there…" But if the

打印前3行也能夠用p命令:

sed -n '3p' prince

可是對於大文件來講,前者比後者效率更高,由於前者讀取到第N行以後就退出了。後者雖然打印了前N行,可是後續的行仍是要繼續讀入,只不會不做處理。

到此爲止,sed基礎命令的部分就介紹完了。

 

6、小結

Linux系統工具衆多,功能也互相重複,這些重複部分的語法還各不相同,好比 grep awk sed 都有正則表達式匹配的功能,可是三者的正則表達式語法就不相同。每一個工具還分 GNU 版和不是 GNU 版,之間的差異也很大,即便都是 GNU 版,那麼版本號的細微差異也會帶來不少差異。

在普通的行處理任務方面,用sed很好,由於命令很簡潔。

 

7、參考

SED入門

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

awk&sed!!!

(完)

相關文章
相關標籤/搜索