sed 是一個流編輯器(stream editor),主要用來執行文字替換。但 sed 的主要設計目的是以批處理的方式而不是互動的方式來編輯檔案。
命令簡介
基本命令格式
sed [常用選項] 命令文字 輸入
常用選項
-n (--quiet, --silent):安靜模式。在 sed 的基本用法中,所有來自標準輸出的資訊都會被列出到終端上。加上 -n 引數後,則只有被sed 處理的那些行才會被輸出。
-e:指定在指令列模式上執行的命令文字。預設不需要指定,只有同時要執行多個命令文字時才需要顯式的指定 -e 選項。
-f:同時要執行多個命令文字時,可以將這些命令文字寫到一個檔案中,然後通過 -f filename 的方式使用。
-r:sed 預設使用基礎正規表示式語法(BRE),指定 -r 選項後使用擴充套件正規表示式語法(ERE)。
-i:直接修改讀取的文件,而不是輸出到終端。
常用命令
a:新增行, a 的後面接字串,這些字串會被新增到匹配行的下面。
c:替換行, c 的後面接字串,這些字串會替換掉匹配到的行。
d:刪除行,刪除匹配到的行。
i:插入行, i 的後面接字串,這些字串會被插入到匹配行的上面。
p:列印,將某些行輸出。通常 p 會與引數 -n 一起使用,這樣只輸出匹配到的行。
s:字串替換,主要搭配正規表示式使用。
解釋一下本文中 "命令" 與 "命令文字" 的區別:
命令是一些抽象的操作,比如 a 指示新增行,d 指示刪除行。
命令文字則是由命令和其它一些資訊組合起來的一個字串,用來執行具體的操作。
比如在第一行下面新增一行,內容為 'Hello world',命令文字為:'1a Hello world'
再如刪除包含字串 'Hello world' 的行,命令文字為:'/Hello world/d'
常用選項及命令詳解
說明:本文示例中 demo 檔案 test.txt 包含三行文字,內容為:
aa bb cc
demo 檔案 hello.txt 包含三行文字,內容為:
Hello world! Hello Jack! Hello China! Hello Nick!
刪除行
刪除行需要使用命令 d:
$ sed '1d' test.txt # 刪除第一行 $ sed '$d' test.txt # 刪除最後一行 $ sed '1,2d' test.txt # 刪除第一行到第二行 $ sed '2,$d' test.txt # 刪除第二行到最後一行
注意,執行完上面的命令,我們只能在命令列終端上看到正確的結果,而 test.txt 檔案根本沒有發生變化:
選項 -i
如果想要直接在原檔案上進行修改(其實是先修改檔案的內容,然後儲存到原檔案中),需要使用選項 -i:
$ sed -i '1d' test.txt
注意,應用 -i 選項後命令列上沒有輸出內容,但是原始檔被更新了。
新增行
a 命令可以在匹配的行下面新增行:
$ sed '1a Hello world!' test.txt # 在第一行下面新增一行,內容為 "Hello world!" $ sed '$a Hello world!' test.txt # 在最後一行下面新增一行,內容為 "Hello world!" $ sed '1,3a Hello world!' test.txt # 在第一行,第二行和第三行下面分別增加一行,內容 # 為 "Hello world!" 1,3 表示從第一行到第三行 $ sed '1a Hello world!\nHello China!' test.txt # 一次增加多行需要使用換行符 \n
選項 -e
-e 選項用來指定命令文字,如果只有一個命令文字時 -e 選項可以省略。如何要指定多個命令文字就需要使用 -e 選項。
$ sed -e '1a xxx' -e '2a yyy' test.txt
插入行
i 命令可以在匹配的行上面插入行,語法與新增行相同,只是新行在指定行的上面(與 a 命令的區別):
選項 -f
前面我們通過選項 -e 新增了多個命令文字,但是如果需要新增比較多的命令文字,使用選項 -e 就不太合適了。因為把所有的命令文字全部寫在命令列中會導致維護困難。此時選項 -f 就派上用場了。我們可以把多個命令文字寫入到文字檔案中,然後通過 -f 選項進行引用。
我們建立一個叫 commands 的檔案,在裡面新增三個命令文字如下:
1i Hello world! 2i Hello world! 3i Hello world!
然後執行命令:
$ sed -f commands test.txt
通過 -f 選項,commands 檔案中的三個命令文字都被執行了!
替換行
使用 c 命令可以輕鬆的進行整行替換:
$ sed '1c Hello world!' test.txt # 把第一行替換為 "Hello world!" $ sed '1,3c Hello world!' test.txt # 把第一行到第三行替換為 "Hello world!"
注意,上圖中的命令把三行文字替換成了一行文字!
字串替換
與行替換不同,s 命令只替換匹配到的內容(一般為字串):
$ sed 's/Hello/Hi/' hello.txt # 把Hello 替換為 Hi
上圖帶給我們的困惑之一是:為什麼第一行中只有第一個 Hello 被替換了?答案是 sed 預設只會替換第一個匹配到的內容!那麼我們的第二個困惑來了:如果只替換第一個匹配到的內容,那麼為什麼第二行和第三行的 Hello 都被替換了呢?這個問題涉及的 sed 的工作方式,sed 是一個以行為單位進行文字處理的工具!所以圖中的三行是被分為三次,每次一行進行處理的。因而第二行和第三行中的 Hello 對於本行來說都是第一個匹配到的內容,被替換是正確的。
要進行全域性替換,需要在命令文字中指定 g,試試下面的命令:
$ sed 's/Hello/Hi/g' hello.txt # 把匹配到的所有Hello 都替換為 Hi
這下第一行中的兩個 Hello 都被替換了。
我們還可以限制執行替換操作的行:
$ sed '2,3s/Hello/Hi/g' hello.txt # 只在第二行和第三行進行替換操作
當然也可以通過替換來刪除不需要的字串:
$ sed 's/Hello//g' hello.txt # 刪除字串 Hello
定界符
雖然 / 是最常用的定界符,但是你也可以使用其它的字元。舉個簡單的例子,當你要在 linux 下進行路徑替換時,使用 / 作為定界符是很不爽的(需要很多的轉義符),此時換一個定界符是最好的解決方案:
上圖中我們使用分號作為定界符輕鬆實現了路徑替換。
匹配
細心的同學可能已經注意到了,sed 所有的操作都是建立在行定位之上的。也就是說無論你要幹什麼,都要先找到(匹配)目標行。連最簡單的刪除行 '1d',也得先定位到第一行,然後才能刪除。所以唯一能限制我們發揮 sed 能力的因素就是:如何匹配到期望的行?
答案是掌握基本的規則,然後多練習! -n 選項和 p 命令是我們練習的好幫手。-n 選項告訴 sed 只輸出那些被處理過的行。比如 sed '1a Hello world!' test.txt 命令預設會輸出四行,應用 -n 後只輸出一行:
p 命令則告訴 sed 只輸出那些匹配到的行, 比如命令:
$ sed -n '1p' test.txt 和命令sed -n '2,3s/Hello/Hi/gp' hello.txt
行匹配的規則大概有兩類:通過行號進行匹配和通過正規表示式進行匹配。
下面是一些通過行號進行匹配的例子:
$ sed -n '1p' test.txt # 匹配第一行 $ sed -n '$p' test.txt # 匹配最後一行 $ sed -n '2,3p' test.txt # 匹配第二行和第三行 $ sed -n '3,$p' test.txt # 匹配第三行和第三行後的每一行
下面是通過正規表示式進行匹配的例子:
$ sed -n '/Hello/p' hello.txt
預設的匹配是區分大小寫的,要忽略大小寫可以使用I(大寫字母i)
$ sed -n '/hello/Ip' hello.txt
下面幾個是正規表示式匹配後執行操作的例子:
$ sed '/Hello/d' hello.txt # 找到匹配的行,並刪除 $ sed '/Hello/a world!' hello.txt # 找到匹配的行,並在它們下面新增新行
$ sed 's/world/China/g' hello.txt # 把行中的 world 替換為 China $ sed '/Hello/s/world/China/g' hello.txt # 找到匹配的行,在這些行中執行替換
下面的例子通過正規表示式進行整行內容的替換:
$ sed -i 's/^commonName.*/commonName = xxx/g' my.cnf $ sed -i 's/^subjectAltName.*/subjectAltName = DNS:xxx/g' my.cnf
總結
sed 是一個強有力的文字替換工具,尤其是需要在自動化的場景中使用的時候。本文結合例項比較詳細的介紹了 sed 的使用方法,希望對大家瞭解、使用 sed 有所幫助。