1. 程式人生 > 實用技巧 >sed 流編輯器

sed 流編輯器


1. sed 簡介:為什麼需要 sed?

在普通文字編輯器(如 vim)中刪除,替換文字可通過鍵盤互動式輸入來執行。但是,如果在自動化場景或者不需要通過文字編輯器的場景下刪除,替換文字該怎麼做呢? sed 編輯器很好的提供了這個功能,sed 編輯器又叫做流編輯器,它基於預先定義的規則來處理資料流,從而實現自動處理文字檔案。

2. sed 命令

sed 包括諸如替換,刪除,插入,附加等多種處理資料流的命令。

2.1 sed 替換

sed 替換的語法如下所示:
[root@lianhua bash]# cat sed_demo.sh
Are you lianhua?
yes! I am lianhua.
hi, lianhua 
nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed 's/lianhua/me/' sed_demo.sh Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# cat sed_demo.sh Are you lianhua? yes! I am lianhua. hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
通過上面示例,有三點值得注意:
  • sed 的 s 標記表示這是一條替換規則,替換規則是使用 me 替換文字中的 lianhua。
  • sed 逐行編輯文字 sed_demo.sh 中的流資料,實際的文字並無變化。事實上,sed 會從 STDIN 輸入流中讀取資料,然後將編輯之後的資料輸出到 STDOUT 中。
  • 如果資料流中有多個匹配項,sed "s/*/*/" 這種形式只會編輯第一個匹配的資料流。
如果需要全域性匹配,則需要在 sed 中加入替換標記,有 4 種替換標記:
  • 數字:表明新文字將替換第幾處匹配的地方。
  • g: 表明新文字將替換所有匹配的文字。
  • p: 表明原先行的內容要打印出來。
  • w file:將替換的結果寫到檔案中。
替換標記 g:
[root@lianhua bash]# sed 's/lianhua/me/g' sed_demo.sh
Are you me?
yes! I am me.
hi, me nice to meet u. I am me sheng.
Nice to meet you too.
可以看到,第三行的兩處 lianhua 都被匹配成 me 了。 替換標記 p:
[root@lianhua bash]# cat sed_demo.sh
...
Are you lianhua?
yes! I am lianhua.
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
 
[root@lianhua bash]# sed 's/lianhua/me/p' sed_demo.sh
...
Are you me?
Are you me?
yes! I am me.
yes! I am me.
hi, me nice to meet u. I am lianhua sheng.
hi, me nice to meet u. I am lianhua sheng.
Nice to meet you too.
 
[root@lianhua bash]# sed -n 's/lianhua/me/p' sed_demo.sh
Are you me?
yes! I am me.
hi, me nice to meet u. I am lianhua sheng.
sed 的 p 標記會列印編輯過的行,通常將它和 sed 的 -n 選項結合使用。 -n 選項禁止 sed 編輯器輸出,而 p 會列印編輯過的行,最終的效果是隻輸出被替換命令修改過的行。 數字替換標記:
[root@lianhua bash]# sed 's/lianhua/me/2' sed_demo.sh
...
Are you lianhua?
yes! I am lianhua.
hi, lianhua nice to meet u. I am me sheng.
Nice to meet you too.
匹配文字中第二次出現的匹配項。 w file 替換標記:
[root@lianhua bash]# sed 's/lianhua/me/w test.txt' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am lianhua sheng.
Nice to meet you too.
[root@lianhua bash]# cat test.txt
Are you me?
yes! I am me.
hi, me nice to meet u. I am lianhua sheng.

2.1.1 sed 替換:單行單命令和單行多命令

前面介紹的替換方式都是單行單命令,即逐行根據單條命令規則編輯資料流。在 sed 編輯器中還可以使用單行多命令,即基於多個規則逐行編輯資料流。 單行多命令主要有兩種形式: 1. 分號,將多條語句用分號隔開,sed 會逐行依序執行語句:
[root@lianhua bash]# sed 's/lianhua/me/g; s/sheng/huaige/' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am me huaige.
Nice to meet you too.
2. 花括號,配合行定址使用:
[root@lianhua bash]# sed '4{s/lianhua/me/g; s/sheng/huaige/}' sed_demo.sh
...
Are you lianhua?
yes! I am lianhua.
hi, me nice to meet u. I am me huaige.
Nice to meet you too.
行定址 4 標記指定了匹配第四行,將花括號中的語句作用於該行,各語句間用分號隔開。

2.1.2 sed 替換:什麼是行定址?

上小節提到了行定址這個概念,那麼什麼是行定址呢? sed 編輯器預設對資料流進行逐行編輯,如果需要指定編輯哪一行,則需要使用行定址來宣告 sed 編輯器該編輯哪一行。 sed 有兩種形式的行定址:
  • 數字形式的行定址。
  • 文字模式過濾器匹配的行定址。

2.1.2.1 數字形式的行定址

數字形式的行定址通過在匹配語句前加數字或行區間來宣告 sed 編輯器的行作用域。 數字形式的行定址:
[root@lianhua bash]# sed '4s/lianhua/me/g' sed_demo.sh
...
Are you lianhua?
yes! I am lianhua.
hi, me nice to meet u. I am me sheng.
Nice to meet you too.
行區間形式的行定址:
[root@lianhua bash]# sed '1,3s/lianhua/me/g' sed_demo.sh
...
Are you me?
yes! I am me.
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
 
[root@lianhua bash]# sed '1,$s/lianhua/me/g' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am me sheng.
Nice to meet you too.
其中,行區間使用逗號分隔,逗號前錶行首,逗號後錶行尾。 $ 標記表示資料流中的最後一行。 可將行區間結合花括號一起使用,構建單行多命令匹配:
[root@lianhua bash]# sed '1,${s/lianhua/me/g; s/sheng/huaige/}' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am me huaige.
Nice to meet you too.

2.1.2.2 文字模式過濾器匹配的行定址

文字模式過濾器匹配的行定址,通過指定文字模式過濾出命令要作用的行,它的格式為:/pattern/command。 示例如下:
[root@lianhua bash]# sed '/lian/s/lianhua/me/' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am lianhua sheng.
Nice to meet you too.
 
[root@lianhua bash]# sed '/lian/{s/lianhua/me/; s/sheng/huaige/}' sed_demo.sh
...
Are you me?
yes! I am me.
hi, me nice to meet u. I am lianhua huaige.
Nice to meet you too.
其中,/lianhua pattern 表示過濾器將過濾出包含文字 lianhua 的行,命令 command 只作用於過濾出的行。同樣的,可以使用花括號構建單行多命令的場景。

2.1.3 sed 替換:這就夠了嗎? - 多行單命令和多行多命令

上節已經介紹了單行單命令和單行多命令的匹配,但是如果匹配的行不在同一行,如下所示:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
要匹配分佈在兩行的 lianhua shuaige,該怎麼匹配呢?這時候就需要用到多行匹配了。 多行匹配會跨行合併文字然後處理合並的文字資料流,sed 包含了三個處理多行文字的特殊命令:
  • N:將資料流中的下一行加入當前行從而建立一個多行組。
  • D:刪除多行組中的一行。
  • P:列印多行組中的一行。
在介紹多行匹配之前,先耐著性子看看 sed 中的 next 命令,通過了解它來打好多行匹配的基礎。

2.1.3.1 next 命令

sed 編輯器中可指定 n 標記將編輯操作移動到匹配行的下一行,從而對下一行進行操作。如下所示:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
 
[root@lianhua bash]# sed '/lianhua/{n; s/shuaige/me/}' sed_demo_d.sh
hi, man
hi
Are you lianhua
me?
yes! I am lianhua
me
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
可以看到,在 sed 命令規則中使用文字模式匹配過濾器匹配 lianhua 這一行,然後花括號多命令列首先使用 next 命令 n 將編輯行移動到匹配行 lianhua 的下一行,然後使用替換命令將改行的 shuaige 替換成 me。並且,可以看到倒數第二行中的 lianhua 並沒有被替換,因為 next 沒有匹配到它。

2.1.3.2 重頭戲:多行合併

從上面 next 命令可知,next 會編輯資料流的下一行。而多行匹配會將下一行資料流合併到當前行,從而操作的是當前行以及下一行的資料流。 1. 多行匹配 N 命令:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
[root@lianhua bash]#
sed 'N; s/lianhua.shuaige/me/' sed_demo_d.sh hi, man hi Are you me? yes! I am me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
使用 N 選項將合併下一行到當前行然後處理,第一行 “hi, man” 和 第二行 “hi” 合併,匹配項中不匹配。第三行和第四行合併,符合匹配項 “lianhua.shuaige”。其中 . 是萬用字元,然後將其替換依次類推... 當使用多行匹配 N 命令時,要特別注意行尾的文字資料流是否可合併,如果行尾的資料流不能作為上一資料流的上一行合併那麼該行將不會被合併:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed 'N; s/lianhua.shuaige/me/' sed_demo_d.sh
hi, man
hi
Are you me?
yes! I am me
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
如上所示,行尾的 “lianhua shuaige”並沒有被合併。解決方法也不難,可以先對行尾的文字匹配,然後在使用多行匹配匹配其它行:
[root@lianhua bash]# sed '$s/lianhua.shuaige/me/; N; s/lianhua.shuaige/me/' sed_demo_d.sh
hi, man
hi
Are you me?
yes! I am me
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
me
2. 多行匹配 D 命令:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed 'N; /lianhua.shuaige/d' sed_demo_d.sh
hi, man
hi
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed 'N; /lianhua.shuaige/D' sed_demo_d.sh
hi, man
hi
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
lianhua shuaige
將單行刪除命令 d 和多行匹配特殊命令 N 結合使用時,d 會將合併的資料流中所有匹配項都刪除。而,使用多行刪除命令 D 時,僅刪除合併資料流的第一行匹配項,且刪除到換行符截至。上例中,第三行 lianhua 所在項被刪除,而第四行匹配項 shuaige 依然存在。 3. 多行列印 P 命令:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed -n 'N; /lianhua.shuaige/p' sed_demo_d.sh
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
 
[root@lianhua bash]# sed -n 'N; /lianhua.shuaige/P' sed_demo_d.sh
Are you lianhua
yes! I am lianhua
將單行列印命令 p 和多行匹配特殊命令 N 結合使用時,p 會將合併資料流的所有匹配項打印出來。而,使用多行列印命令 P 時,僅打印合並資料流的第一行匹配項,且列印到換行符截至。

2.1.3.3 多行單命令和多行多命令

上面兩節介紹的都是多行單命令,那麼類似的,多行合併也有多命令的形式。它們是:分號分隔多條語句和文字模式匹配加花括號的形式: 1. 分號分隔多條語句:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed 'N; s/lianhua.shuaige/me/; s/hi/hello/' sed_demo_d.sh
hello, man
hi                   # 想一想,這裡的 hi 為什麼沒有替換呢?
Are you me?
yes! I am me
hello, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
2. 文字模式匹配加花括號:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed '/lianhua/{N; s/lianhua.shuaige/me/}' sed_demo_d.sh
hi, man
hi
Are you me?
yes! I am me
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige

2.1.4 sed 替換:特殊字元的替換

替換是 sed 編輯器很常用的命令,除了普通字元的替換通常也會包含特殊字元的替換,如正斜線,空格,換行符,單雙引號等的替換。 1. 正斜線替換:
[root@lianhua bash]# cat sed_demo_s.sh
hi, man
hi
 
what's the directory right now?
the directory is /home/lianhua/
 
what's you address?
well, my address is https://10.10.10.10
 
no,man, i mean the physical address
ok, my physical address is "10.10.10.10"
 
no,man, i mean your home address
haha, i got your point, my home address is
'00 01 6C 06 A6 00'
 
you haven't got my point...
[root@lianhua bash]# sed -n '/directory/{s/\/home\/lianhua\//home/p}; s/https:\/\//ftp:/p' sed_demo_s.sh
the directory is home
well, my address is ftp:10.10.10.10
當替換正斜線時,需使用反斜槓進行轉義。有時候這樣的寫法會帶來一些困惑,sed 允許使用其它字元來代替字串分隔符,從而使得替換更簡潔:
[root@lianhua bash]# sed -n '/directory/{s!/home/lianhua/!home!p}; s/https:\/\//ftp:/p' sed_demo_s.sh
the directory is home
well, my address is ftp:10.10.10.10
 
[root@lianhua bash]# sed -n '/directory/{s#/home/lianhua/#home#p}; s/https:\/\//ftp:/p' sed_demo_s.sh
the directory is home
well, my address is ftp:10.10.10.10
2. 空格替換:
[root@lianhua bash]# sed -n '/my home address/{n ; s/ /:/gp}' sed_demo_s.sh     # 替換分隔符中間保留空格即為匹配項
'00:01:6C:06:A6:00'
3. 單引號替換:
[root@lianhua bash]# sed -n '/my home address/{n ; s/ /:/g; s/'//pg}' sed_demo_s.sh
> '
sed: -e expression #1, char 40: extra characters after command
 
[root@lianhua bash]# sed -n '/physical address/{n ; s/\'//gp}' sed_demo_s.sh
> '
sed: -e expression #1, char 46: unterminated `s' command
 
[root@lianhua bash]# sed -n "/my home address/{n ; s/ /:/g; s/'//pg}" sed_demo_s.sh
00:01:6C:06:A6:00
單引號替換需將 sed 編輯規則置於雙引號中,而不是放在單引號裡,放在單引號內內層的單引號將無法被識別。並且,單引號也不支援轉義表示。 4. 雙引號替換:
[root@lianhua bash]# sed -n '/physical address/{n ; s/"//gp}' sed_demo_s.sh
ok, my physical address is 10.10.10.10
 
[root@lianhua bash]# sed -n "/physical address/{n ; s/\"//gp}" sed_demo_s.sh
ok, my physical address is 10.10.10.10
雙引號替換可將 sed 編輯規則置於單引號內,或者使用反斜槓對雙引號轉義也是可以的。

2.2 sed 刪除

前面花了很多篇幅介紹 sed 中的替換命令,當然 sed 編輯器也支援其它命令。sed 的 d 命令表示刪除,它會根據預定義規則逐行刪除匹配項:
[root@lianhua bash]# cat sed_demo_d.sh
hi, man
hi
Are you lianhua
shuaige?
yes! I am lianhua
shuaige
hi, lianhua nice to meet u. I am lianhua sheng.
Nice to meet you too.
lianhua shuaige
 
[root@lianhua bash]# sed '/lianhua/d' sed_demo_d.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.
sed 匹配到含有字元 lianhua 的行,然後刪除這些行。

2.3 sed 插入和附加

sed 中的 i 命令表示插入,a 表示附加。它們的區別時插入會在匹配行的上一行插入文字,而附加則是在匹配行的下一行附加文字。
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.
 
[root@lianhua bash]# cat sed_demo_dd.sh | sed '3i\are you' | sed "5a\no i'm not.."
hi, man
hi
are you
shuaige?
shuaige
no i'm not..
Nice to meet you too.

2.4 sed 修改

sed 中的 c 命令表示修改,它會修改資料流中整行的內容。
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.
 
[root@lianhua bash]# cat sed_demo_dd.sh | sed '3c\lianhua?' | sed '/shuaige/c\lianhua'      # 同時用到了數字定址和文字匹配定址
hi, man
hi
lianhua?
lianhua
Nice to meet you too.

2.5 sed 特殊命令

sed 除了替換,刪除等命令外,還包括一些特殊命令,如排除命令 !和行號命令 = 等: 1. 排除命令 在命令前加排除命令 ! 表示預定義規則作用於除匹配行之外的其它行:
[root@lianhua ~]$ openstack port list | sed '/demo/!d'
| 40d7-9c56-0a419c766316 | demo       | fa:16:3e:f7:91:89 | ip_address='192.168.2.131', subnet_id='afd835f7f6ee'  | ACTIVE |
2. 行號命令 sed 中使用 = 可顯示每行的行號,通常將它和替換命令一起使用:
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.
 
[root@lianhua bash]# cat sed_demo_dd.sh | sed '='
1
hi, man
2
hi
3
shuaige?
4
shuaige
5
Nice to meet you too.
 
[root@lianhua bash]# cat sed_demo_dd.sh | sed '=' | sed 'N; s/\n/ /'
1 hi, man
2 hi
3 shuaige?
4 shuaige
5 Nice to meet you too.

2.6 sed 特殊字元

sed 中也包括一些特殊字元,如表示空白行的字元 ^$:
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
 
shuaige
 
Nice to meet you too.
 
[root@lianhua bash]# sed 's/\n/a/' sed_demo_dd.sh          # 空白字元,不是換行符,使用換行符並不能匹配到它
hi, man
hi
shuaige?
 
shuaige
 
Nice to meet you too.
 
[root@lianhua bash]# sed 's/^$/a/' sed_demo_dd.sh
hi, man
hi
shuaige?
a
shuaige
a
Nice to meet you too.
 
[root@lianhua bash]# sed '/^$/d' sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.

3. sed 的兩種空間:模式空間和保持空間

sed 編輯器有兩種非常重要的空間:模式空間和保持空間。模式空間是 sed 編輯器在執行命令時儲存待檢查文字的空間。保持空間是 sed 編輯器在處理模式空間中的某些行時,用來臨時儲存一些行的空間。 有以下幾個命令可以用來做為模式空間和保持空間的互相轉化:
  • h:將模式空間複製到保持空間。
  • H:將模式空間附加到保持空間。
  • g:將保持空間複製到模式空間。
  • G:將保持空間附加到模式空間。
  • x:交換模式空間和保持空間的內容。
舉例如下:
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
 
[root@lianhua bash]# sed -n '/shuaige?/{h; p; n; p; g; p}' sed_demo_dd.sh
shuaige?
shuaige
shuaige?
上例中,使用文字匹配過濾出 "shuaige?" 行,在該行通過 h 命令將模式空間的 "shuaige?" 複製到保持空間,p 命令列印模式空間的文字行,接著 n 向下移動一行,p 列印該行即 shuaige,然後 g 命令將保持空間的內容複製到模式空間,在通過 p 命令列印模式空間的當前內容,即 "shuaige?"。 結合排除命令可以實現翻轉文字的效果:
[root@lianhua bash]# cat sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.
 
[root@lianhua bash]# sed -n '{1!G; h; $p}' sed_demo_dd.sh
Nice to meet you too.
shuaige
shuaige?
hi
hi, man
使用 Liunx 自帶的 tac 命令也可以翻轉文字:
[root@lianhua bash]# tac sed_demo_dd.sh
Nice to meet you too.
shuaige
shuaige?
hi
hi, man
組合使用 h G 命令看看會發生什麼效果,思考這是為什麼:
[root@lianhua bash]# sed -n '{h; G; p}' sed_demo_dd.sh
hi, man
hi, man
hi
hi
shuaige?
shuaige?
shuaige
shuaige
Nice to meet you too.
Nice to meet you too.
 
[root@lianhua bash]# sed -n '{h; p; G}' sed_demo_dd.sh
hi, man
hi
shuaige?
shuaige
Nice to meet you too.