一、流編輯器 sed 與命令 sed Linux 中,常使用流編輯器 sed 進行文本替換工作。與常使用的互動式編輯器(如vim)不同,sed 編輯器以批處理的方式來編輯文件,這比互動式編輯器快得多,可以快速完成對數據的編輯修改。 一般來說,sed 編輯器會執行以下操作: 1)一次從輸入中讀取一行數 ...
一、流編輯器 sed 與命令 sed
Linux 中,常使用流編輯器 sed 進行文本替換工作。與常使用的互動式編輯器(如vim)不同,sed 編輯器以批處理的方式來編輯文件,這比互動式編輯器快得多,可以快速完成對數據的編輯修改。
一般來說,sed 編輯器會執行以下操作:
1)一次從輸入中讀取一行數據;
2)根據所提供的編輯器命令匹配數據;
3)按照命令修改流中的數據;
4)將新的數據輸出到 STDOUT。
在 sed 編輯器匹配完一行數據後,它會讀取下一行數據並重覆這個過程,直到處理完所有數據。使用 sed 命令打開一個 sed 編輯器。sed 命令的格式如下:
sed [options] edit_commands [file] # [ ] 中的內容為可選可不選
其中,options 為命令選項,選擇不同的 options 可以修改 sed 命令的行為,主要有 3 個選項:
1)-e 選項: 在處理輸入時,將 script 中指定的編輯命令添加到已有的命令中。通俗的說,就是在 sed 後面直接添加編輯命令:
sed -e 'edit_commands' [files]
sed 命令在預設情況下使用的是 -e 選項。當只有一個編輯命令時,-e 選項可以省略;但是當要在一條 sed 語句中執行多個編輯命令時,就需要使用 -e 選項了:
sed -e 's/root/ROOT/g; s/bin/BIN/g' /etc/passwd # 使用 sed 同時執行兩條編輯命令(本文大部分用例都直接使用 /etc/passwd 文件) sed -e 's/root/ROOT/g' -e 's/bin/BIN/g' /etc/passwd # 使用 sed 同時執行兩條編輯命令
2)-f 選項:在處理輸入時,將 file 中指定的編輯命令添加到已有的命令中:
前面提到,在需要同時執行多條編輯命令時,可以使用 -e 選項。但是當所需要執行的編輯命令數量很多時,每次使用 sed 時一行一行地敲顯然不是很方便,這時可以將所用到的 sed 編輯命令寫入一個文件,然後使用 sed -f 選項來指定讀取該文件:
$ cat script.sed $ s/root/ROOT/ $ s/bin/BIN/ $ s/home/HOME/
sed -f script.sed /etc/passwd
3)-n 選項: 不產生命令輸入:
sed -n 's/root/ROOT/' /etc/passwd
使用 -n 選項不會將流編輯器的內容輸出到 STDOUT,通常將 -n 選項與 p 命令結合起來使用,以只列印被匹配的行。
除了這三個選項外,sed 編輯器還提供了許多命令,用來進行更詳細的操作,簡單列一下,後面再仔細介紹:
命令 | 描述 |
s | 文本替換操作 |
d | 刪除操作 |
i | 插入操作 |
a | 附加操作 |
c | 將一行文本修改為新的行 |
y | 逐字元替換 |
p | 列印文本行 |
= | 列印行號 |
w | 向文件中寫入數據 |
r | 從文件中讀取數據 |
二、使用 sed 命令進行文本替換
sed 使用 s 命令來進行文本替換操作,基本格式如下:
sed 's/srcStr/dstStr/' file
其中,srcStr 為想要替換的文本,dstStr 為將要替換成的文本。使用 s 命令時,sed 編輯器會在一行一行地讀取文件 file,併在每行查找文本 srcStr,如果找到了,則將該處的 srcStr 替換為 dstStr。
/ 字元為界定符,用於分隔字元串(sed 編輯器允許使用其他字元作為替換命令中的字元串分隔符):
sed 's!/bin/bash!/BIN/BASH!' /etc/passwd # 使用 ! 作為字元串分隔符
預設情況下,替換命令只會替換掉目標文本在每行中第一次出現的地方。若想要替換掉每行中所有匹配的地方,可以使用替換標記 g。替換標記放在編輯命令的末尾。除了 g 外,還有幾種替換標記:
1)數字:指明替換掉第幾次匹配到的文本,沒有設置這個標記時,預設是替換第一次匹配的文本:
sed 's/root/ROOT/2' /etc/passwd
這行命令將 /etc/passwd 文件中每行的第 2 個 root 替換為 ROOT;
2)g :替換所有匹配到的文本:
sed 's/root/ROOT/g' /etc/passwd
這行命令將 /etc/passwd 文件中的 root,全部替換為 ROOT;
3)p :列印與替換命令中指定模式(srcStr)相匹配的行:
sed 's/root/ROOT/p' /etc/passwd
執行這命令,會在 STDOUT 上看到包含有 root 的行被輸出了兩次,一次是 sed 編輯器自動輸出的;另一次則是 p 標記列印出來的匹配行。
單獨地使用 p 標記沒什麼用處,通常將 p 標記和 -n 選項結合起來使用,這樣就可以只輸出被匹配替換過的行了:
sed -n 's/root/ROOT/gp' /etc/passwd # 將 /etc/passwd 中所有的 root 都替換成 ROOT,並輸出被修改的行
註:可以使用 " = " 命令來列印行號,用法與 p 一樣。
4)w file :將替換的結果寫到文件中,不過只保存被修改的行,與 -n + p 的功能類似:
sed -n 's/root/ROOT/g w change.txt' /etc/passwd # 將 /etc/passwd 中所有的 root 都替換成 ROOT,並將被修改的行保存到文件 change.txt 中去
三、使用行定址對特定行進行編輯
預設情況下,sed 編輯器會對文件中的所有行進行編輯。當然,也可以只指定特定的某些行號,或者行範圍來進行流編輯,這需要用到行定址。所指定的行地址放在編輯命令之前:
[address] commands
3.1 使用數字方式進行行定址
sed 編輯器將文本流中的每一行都進行編號,第一行的編號為 1 ,後面的按順序分配行號。通過指定特定的行號,可以選擇編輯特定的行。舉幾個例子:
sed '3 s/bin/BIN/g' /etc/passwd # 將第3行中所有的 bin 替換成 BIN
sed '2,5 s/bin/BIN/g' /etc/passwd # 將第2到5行中所有的 bin 替換成 BIN sed '10,$ s/bin/BIN/g' /etc/passwd # 將第10行到最後一行中所有的 bin 替換成 BIN
註:行定址不止對替換命令有效,對其他命令也都是有效的,後面也會用到。
3.2 使用文本模式過濾器過濾行
sed 編輯器允許指定文本模式來過濾出命令要作用的行,格式如下:
/pattern/command
必須使用斜杠符 " / " 將要指定的文本模式 pattern 包含起來。sed 編輯器會尋找匹配文本模式的行,然後對這些行執行編輯命令:
sed -n '/root/s/bin/BIN/p' /etc/passwd # 尋找包含有字元串 root 的行,並將匹配行的 bin 替換為 BIN
與數字定址一樣,也可以使用文本過濾區間來過濾行:
sed '/pattern1/,/pattern2/ edit_command' file
這行命令會在文件 file 中先尋找匹配 pattern1 的行,然後從該行開始,執行編輯命令,直到找到匹配 pattern2 的行。但是需要註意的是,使用文本區間過濾文本時,只要匹配到了開始模式(pattern1),編輯命令就會開始執行,直到匹配到結束模式(pattern2),這會導致一種情況:一個文本中,先匹配到了一對 pattern1、pattern2,對該文本區間中的文本執行了編輯命令;然後,在 pattern2 之後又匹配到了 pattern1,這時就會再次開始執行編輯命令,因此,在使用文本區間過濾時要格外小心。舉個例子:
sed -n '/root/,/nologin/ s/bin/BIN/p' /etc/passwd
這行命令對 /etc/passwd 進行了兩次文本區間匹配,結果如下:
四、使用 sed 命令刪除行
sed 編輯器使用 d 命令來刪除文本流中的特定行。使用 d 命令時,一般需要帶上位定址,以刪除指定的行,否則預設會刪除所有文本行:
sed '/root/d' /etc/passwd # 刪除匹配 root 的行 sed '2,$d' /etc/passwd # 刪除第2到最後一行
五、使用 sed 命令插入和附加文本
sed 編輯器使用 i 命令來向數據流中插入文本行,使用 a 命令來向數據流中附加文本行。其中:i 命令會在指定行前增加一個新行;a 命令會在指定行後增加一個新行。
需要註意的是,這兩個命令都不能在單個命令行上使用(即不是用來在一行中插入或附加一段文本的),只能指定插入還是附加到另一行。命令格式如下:
sed '[address][i | a]\newline' file
newline 中的文本即為將要插入或附加在一行前面或後面的文本。常常使用這兩個命令結合行定址在特定的行前面或後面增加一個新行。舉個例子:
sed 'i\Insert a line behind every line' /etc/passwd # 向數據流的每一行前面增加一個新行,新行的內容為 \ 後面的內容 sed '1i\Insert a line behind the first line' /etc/passwd # 在數據流的第一行前面增加一個新行 sed '3a\Append a line after the third line' /etc/passwd # 在數據流的第三行後面增加一個新行 sed '$a\Append a line in the last line' /etc/passwd # 在數據流的最後一行後面增加一個新行
六、使用 sed 命令修改行
使用命令 c 可以將數據流中的整行文本修改為新的行,與插入、附加操作一樣,這要求在 sed 命令中指定新的行,格式如下:
sed '[address][c]\newtext' file
newtext 中的文本為匹配行將要被修改成的文本。
sed '3 c\New text' /etc/passwd # 將數據流中第三行的內容修改為 \ 後面的內容 sed '/root/ c\New text' /etc/passwd # 將匹配到 root 的行的內容修改為 \ 後面的內容 sed '2,4c\New text' /etc/passwd # 將第2到4行的內容修改為 \ 後面的內容,但是不是逐行修改,而是會將這之間的 3 行用一行文本來替代
註意這裡對地址區間使用 c 命令進行修改時,不會逐行修改,而是會將整個區間用一行修改文本替代。
七、使用 sed 命令逐字元轉換
使用 y 參數可以按要求對文本進行逐字元轉換。格式如下:
[address]y/inchars/outchars/
轉換命令會對 inchars 和 outchars 的值進行一對一的映射。inchars 中的第一個字元會被轉換成 outchars 中的第一個字元;inchars 中的第二個字元會被轉換成 outchars 中的第二個字元;... 直到處理完一行。如果 inchars 和 outchars 的長度不同,則 sed 編輯器會產生一個錯誤消息。舉個例子:
echo abcdefggfedcba | sed 'y/acg/ACG/'
輸出結果為 AbCdefGGfedCbA。
八、使用 sed 命令處理文件
8.1 向文件中寫入數據
前面已經提到過,可以使用 w 命令向文件寫入行。格式如下:
[address]w filename
舉個例子:
sed '1,2w test.txt' /etc/passwd
該語句將數據流的第 1、2 行寫入文件 test.txt 中去。
8.2 從文件中讀取數據
可以使用 r 命令來將一個文本中的數據插入到數據流中去,與普通的插入命令 i 類似,這也是對行進行操作的,命令格式如下:
[address]r filename
filename 為要插入的文件。r 命令常結合行定址使用,以將文本插入到指定的行後面。舉個例子:
sed '3 r test.txt' /etc/passwd
這句話將文件 test.txt 中的內容插入到數據流第三行後面去。
參考書籍:
《Linux命令行與shell腳本編程大全》 (第3版)
《Shell腳本學習指南》