sed 編輯器被稱作流編輯器(stream editor),與普通的交互式文本編輯器截然不同。在交互式文本編輯器(比如Vim)中,可以用鍵盤命令交互式地插入、刪除或替換文本數(shù)據(jù)。流編輯器則是根據(jù)事先設(shè)計(jì)好的一組規(guī)則編輯數(shù)據(jù)流。
一、命令介紹
命令語(yǔ)法
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
#如:
sed [選項(xiàng)] '匹配條件和操作指令' 文件名
cat 文件名 | sed [選項(xiàng)] '匹配條件和操作指令'
命令選項(xiàng)
| 選項(xiàng) | 描述 |
|---|---|
-n, --quiet, --silent禁止自動(dòng)打印模式(常配合'p'使用,僅顯示處理后的結(jié)果) |
sed -n '/hello/p' filename使用 /hello/ 匹配含有 "hello" 的行,p 打印匹配的行 |
--debug以注解的方式顯示 sed 的執(zhí)行過(guò)程,幫助調(diào)試腳本 |
sed --debug 's/foo/bar/' filename當(dāng)你使用 sed 修改內(nèi)容時(shí),它會(huì)顯示調(diào)試信息,以便你了解腳本是如何執(zhí)行的 |
-e script, --expression=script在命令行中直接指定 sed 腳本(允許多個(gè) sed 表達(dá)式) |
sed -e 's/foo/bar/' -e 's/hello/world/' filename將文件中的 foo 替換為 bar,然后將 hello 替換為 world |
-f script-file, --file=script-file從指定的腳本文件中讀取 sed 命令 |
sed -f script.sed filenamescript.sed 是包含多個(gè) sed 命令的腳本文件,sed 會(huì)按順序執(zhí)行這些命令 |
--follow-symlinks當(dāng)指定 -i 時(shí),sed 會(huì)跟隨符號(hào)鏈接(symlink)指向的實(shí)際文件進(jìn)行編輯 |
sed -i --follow-symlinks 's/foo/bar/' symlink.txt如果 symlink.txt 是一個(gè)符號(hào)鏈接文件,sed 會(huì)編輯它指向的實(shí)際文件 |
-i[SUFFIX], --in-place[=SUFFIX]直接編輯文件(如果提供 SUFFIX,則進(jìn)行備份) |
sed -i.bak 's/foo/bar/' filename直接在 filename 中將 foo 替換為 bar,并創(chuàng)建一個(gè)備份文件 filename.bak |
-l N, --line-length=N當(dāng)使用 l 命令(列出行內(nèi)容)時(shí),指定輸出的行寬(N 表示字符數(shù)) |
echo 'hello world' | sed -l 5 'l' 使用 l 命令顯示 "hello world",但每行最多顯示 5 個(gè)字符 |
--posix禁用 GNU 擴(kuò)展,使 sed 遵循 POSIX 標(biāo)準(zhǔn)語(yǔ)法 |
sed --posix 's/foo/bar/' filename這將禁用 sed 的一些非標(biāo)準(zhǔn)特性,確保腳本在 POSIX 環(huán)境下工作 |
-E, -r, --regexp-extended使用擴(kuò)展的正則表達(dá)式(ERE),這與基本正則表達(dá)式(BRE)相比,簡(jiǎn)化了一些語(yǔ)法(例如不用轉(zhuǎn)義括號(hào)和 +) |
echo "abc123" | sed -E 's/[a-z]+([0-9]+)/\1/'使用擴(kuò)展正則表達(dá)式,匹配并提取字母后面的數(shù)字 |
-s, --separate將多個(gè)輸入文件視為獨(dú)立的流,而不是作為一個(gè)連續(xù)的流處理 |
sed -s 's/foo/bar/' file1.txt file2.txtsed 會(huì)分別處理 file1.txt 和 file2.txt,而不是將它們作為一個(gè)整體處理 |
--sandbox以沙盒模式運(yùn)行,禁止使用 e, r, w 命令,防止 sed 修改文件或執(zhí)行外部命令 |
sed --sandbox 's/foo/bar/' filename啟用沙盒模式,防止 sed 腳本執(zhí)行危險(xiǎn)的操作 |
-u, --unbuffered減少?gòu)妮斎胛募x取數(shù)據(jù)時(shí)的緩沖區(qū)大小,并更頻繁地刷新輸出 |
sed -u 's/foo/bar/' filename立即將處理結(jié)果輸出到標(biāo)準(zhǔn)輸出,而不是等到處理大量數(shù)據(jù)后再輸出 |
-z, --null-data將輸入中的行分隔符從換行符 \n 改為 NUL 字符 \0,這在處理二進(jìn)制數(shù)據(jù)或以 NUL 作為分隔符的文本時(shí)很有用 |
sed -z 's/foo/bar/' filename使用 NUL 字符作為行分隔符處理文本 |
匹配條件
默認(rèn)情況下,在sed中使用的命令會(huì)作用于文本數(shù)據(jù)的所有行。如果只想將命令用于特定的行或者某些行,則需要使用行尋址功能,在sed中包含兩種形式的行尋址。
- 以數(shù)字形式表示的行空間
- 以文本模式來(lái)過(guò)濾行
格式如下:
| 選項(xiàng) | 描述 |
|---|---|
/pattern /command |
匹配數(shù)據(jù)行(支持正則表達(dá)式) |
/pattern1/,/pattern2/ command |
匹配從pattern1開(kāi)始到pattern2結(jié)束的行(支持正則表達(dá)式) |
n(數(shù)字) command |
使用“行號(hào)”匹配,范圍是1-(表示最后一行) |
addr1,addr2 command |
使用“行號(hào)或正則”定位,匹配從addr1到addr2的所有行 |
addr1,+n command |
使用“行號(hào)或正則”定位,匹配從addr1開(kāi)始及后面的n行 |
n~step command |
使用“行號(hào)”,匹配從行號(hào)開(kāi)始開(kāi)始,步長(zhǎng)為step的所有數(shù)據(jù)行 |
正則表達(dá)式元字符:
與grep一樣,sed也支持特殊元字符,來(lái)進(jìn)行模式查找、替換。不同的是,sed使用的正則表達(dá)式是括在斜杠線"/"之間的模式。
如果要把正則表達(dá)式分隔符"/"改為另一個(gè)字符,比如o,只要在這個(gè)字符前加一個(gè)反斜線,在字符后跟上正則表達(dá)式,再跟上這個(gè)字符即可。例如:sed -n '\o^Myop' datafile
| 選項(xiàng) | 描述 |
|---|---|
.匹配除換行符以外的單個(gè)字符 |
/m..y/ 匹配包含字母m,后跟兩個(gè)任意字符,再跟字母y的行 |
*匹配零個(gè)或多個(gè)前導(dǎo)字符 |
/my*/ 匹配包含字母m,后跟零個(gè)或多個(gè)y字母的行 |
[]匹配指定字符組內(nèi)的任一字符 |
/[Mm]y/ 匹配包含My或my的行 |
[^]匹配不在指定字符組內(nèi)的任一字符 |
/[^Mm]y/ 匹配包含y,但y之前的那個(gè)字符不是M或m的行 |
x\{m\}連續(xù)m個(gè)x |
/9{5}/ 匹配包含連續(xù)5個(gè)9的行 |
x\{m,\}至少m個(gè)x |
/9{5,}/ 匹配包含至少連續(xù)5個(gè)9的行 |
x\{m,n\}至少m個(gè),但不超過(guò)n個(gè)x |
/9{5,7}/ 匹配包含連續(xù)5到7個(gè)9的行 |
^行首定位符 |
/^my/ 匹配所有以my開(kāi)頭的行 |
$行尾定位符 |
/my$/ 匹配所有以my結(jié)尾的行 |
....保存已匹配的字符 |
1,20s/youyouself/\1r/ 標(biāo)記元字符之間的模式,并將其保存為標(biāo)簽1,之后可以使用\1來(lái)引用它。最多可以定義9個(gè)標(biāo)簽,從左邊開(kāi)始編號(hào),最左邊的是第一個(gè)。此例中,對(duì)第1到第20行進(jìn)行處理,you被保存為標(biāo)簽1,如果發(fā)現(xiàn)youself,則替換為your。 |
&保存查找串以便在替換串中引用 |
s/my/&/ 符號(hào)&代表查找串。my將被替換為my |
\<詞首定位符 |
/<my/ 匹配包含以my開(kāi)頭的單詞的行 |
\>詞尾定位符 |
/my>/ 匹配包含以my結(jié)尾的單詞的行 |
定界符
/ 在sed中作為定界符使用,也可以使用任意的定界符。
sed 's|foo|bar|g' filename
sed 's:foo:bar:g' filename
#定界符出現(xiàn)在樣式內(nèi)部時(shí),需要進(jìn)行轉(zhuǎn)義:
sed 's/\/bin/\/usr\/bin/g' filename
變量引用
sed表達(dá)式使用單引號(hào)來(lái)引用,但是如果表達(dá)式內(nèi)部包含"變量"字符串,則需要使用雙引號(hào)。
foo="world"
echo "hello world" | sed "s/$foo/librarookie"
hello librarookie
操作指令
| 選項(xiàng) | 描述 |
|---|---|
! <script>表示后面的命令,對(duì) "所有沒(méi)有被選定" 的行發(fā)生作用 |
sed -n '/foo/!p' filename只打印不含有 "foo" 的行 |
p打?。╬rint)當(dāng)前匹配的數(shù)據(jù)行(常配合 -n 使用) |
sed -n '/foo/p' filename只打印含有 "foo" 的行 |
l小寫L,打印當(dāng)前匹配的數(shù)據(jù)行,并顯示控制字符,如回車符\,結(jié)束符$等 |
sed -n '/foo/l' filename只打印含有 "foo" 的行,并顯示控制字符 |
=打印當(dāng)前讀取的數(shù)據(jù)所在的 "行數(shù)" |
sed -n '$=' filename<br>打印文件最后一行的行數(shù) |
a\ <text>在匹配的數(shù)據(jù)行 "后" 追加(append)文本內(nèi)容 |
sed '/foo/a\hello librarookie' filename在每個(gè)含有 "foo" 行,下面追加一行 "hello librarookie" |
i\ <text>在匹配的數(shù)據(jù)行 "前" 插入(insert)文本內(nèi)容 |
sed '/foo/i\hello librarookie' filename在每個(gè)含有 "foo" 行,上面插入一行 "hello librarookie" |
c\ <text>將匹配的數(shù)據(jù)行 "整行" 內(nèi)容更改(change)為特定的文本內(nèi)容 |
sed '/foo/c\hello librarookie' filename將每個(gè)含有 "foo" 整行,替換為 "hello librarookie" |
d行刪除,刪除(delete)匹配的數(shù)據(jù)行整行內(nèi)容 |
sed '/foo/d' filename刪除每個(gè)含有 "foo" 的行 |
r <filename>從文件中讀取(read)數(shù)據(jù),并追加到匹配的數(shù)據(jù)行后面 |
sed -i '/foo/r datafile' filename將文件 datafile 的內(nèi)容,追加在文件 filename 中每個(gè)含有 "foo" 的行下面 |
w <filename>將當(dāng)前匹配到的數(shù)據(jù),寫入(write)特定的文件中 |
sed -n '/foo/w newfile' filename將文件 filename 中每個(gè)含有 "foo" 的行,寫入到新文件 newfile 里面 |
q [exit code]立刻退出(quit)sed腳本 |
sed '5q' filename打印第 5 行前的數(shù)據(jù),類似 sed -n '1,5p |
s/regexp/replace/使用正則匹配,將匹配的數(shù)據(jù)替換(substitute)為特定的內(nèi)容 |
sed 's/foo/bar/' filename將每個(gè)含有 "foo" 行中,第一次出現(xiàn)的 "foo",替換為 "bar" |
n(數(shù)字)只替換第 n 次出現(xiàn)的匹配項(xiàng) |
sed 's/foo/bar/2' filename將每個(gè)含有 "foo" 行中,第 2 次出現(xiàn)的 "foo",替換為 "bar" |
g全局替換(Global substitution):替換每一行中的所有匹配項(xiàng) |
sed 's/foo/bar/g' filename將每行中的 "foo" ,替換為 "bar" |
i忽略大小寫(Ignore case):進(jìn)行不區(qū)分大小寫的匹配 |
sed '/foo/i' filename打印含有 "foo" 或 "FOO" 的行 |
e執(zhí)行(Execute):允許在替換文本中進(jìn)行命令執(zhí)行 |
echo "ls /tmp" | sed 's/ls/ls -l/e'將sed命令的結(jié)果,當(dāng)作命令執(zhí)行;即輸出 ls -l /tmp 命令的結(jié)果 |
&引用匹配的整個(gè)字符串 |
echo "hello" | sed 's/hello/& librarookie/'將每行中的 "foo" ,替換為 "bar" |
高級(jí)操作指令
| 選項(xiàng) | 描述 |
|---|---|
h |
將 "模式空間" 中的數(shù)據(jù)復(fù)制到 "保留空間" |
H |
將 "模式空間" 中的數(shù)據(jù)追加到 "保留空間" |
g |
將 "保留空間" 中的數(shù)據(jù)復(fù)制到 "模式空間" |
G |
將 "保留空間" 中的數(shù)據(jù)追加到 "模式空間" |
x |
將 "模式空間" 和 "保留空間" 中的數(shù)據(jù)交換 |
n |
讀取下一行數(shù)據(jù)復(fù)制到 "模式空間" |
N |
讀取下一行數(shù)據(jù)追加到 "模式空間" |
:label |
為 t 或 b 指令定義1abel標(biāo)簽 |
t label |
有條件跳轉(zhuǎn)到標(biāo)簽(1abel),如果沒(méi)有 1abel ,則跳轉(zhuǎn)到指令的結(jié)尾 |
b label |
跳轉(zhuǎn)到標(biāo)簽(1abel),如果沒(méi)有 label ,則跳轉(zhuǎn)到指令的結(jié)尾 |
y/源/目標(biāo)/ |
以字符為單位將源字符轉(zhuǎn)為為目標(biāo)字符 |
二、常用實(shí)例
2.1 匹配范圍
#行號(hào)范圍:打印第3行到第5行
sed -n '3,5p' filename
#正則表達(dá)式匹配范圍:從匹配start_pattern的行開(kāi)始,到匹配end_pattern的行結(jié)束
sed -n '/start_pattern/,/end_pattern/p' filename
#命令組合:從第1行開(kāi)始,到第一個(gè)空白行為止
sed -n '1,/^$/p' filename
#倒數(shù)行范圍:從第1行開(kāi)始,直到匹配end_pattern的行之前(使用!進(jìn)行取反)
sed -n '1,/end_pattern/!p' filename
#指定行的倍數(shù):打印所有奇數(shù)行(從第一行開(kāi)始,步長(zhǎng)為 2)
sed -n '1~2p' filename
2.2 基礎(chǔ)替換
#只替換文本中第一次出現(xiàn)的匹配項(xiàng),并將結(jié)果輸出到標(biāo)準(zhǔn)輸出
sed 's/foo/bar/' filename
#只替換每行中第三次出現(xiàn)的匹配項(xiàng)
sed 's/foo/bar/3' filename
#只打印替換過(guò)的行
sed -n 's/foo/bar/p' filename
#編輯原文件,同時(shí)創(chuàng)建 filename.bak 備份
sed -i.bak 's/foo/bar/' filename
#忽略大小寫進(jìn)行替換
sed 's/foo/bar/i' filename
#全局替換
sed 's/foo/bar/g' filename
#全局替換,每行替換從第 2 次開(kāi)始出現(xiàn)的匹配項(xiàng)
sed 's/foo/bar/2g' filename
2.3 組合替換
#替換并寫入新文件:將替換過(guò)的行寫入 output.txt
sed 's/foo/bar/w output.txt' filename
#結(jié)合標(biāo)記符:在匹配的字符串后添加后綴
sed 's/foo/&.bak/' filename
#執(zhí)行sed結(jié)果的命令(謹(jǐn)慎使用,可能導(dǎo)致安全風(fēng)險(xiǎn))
sed 's/systemctl start/systemctl status/e' filename
echo "ls /tmp" | sed 's/ls/ls -l/e'
#行號(hào)范圍:將第3行到第5行中的"foo"替換為"bar"
sed '3,5s/foo/bar/g' filename
#正則表達(dá)式匹配范圍:從包含"start"的行開(kāi)始,到包含"end"的行結(jié)束,替換"foo"為"bar"
sed '/start/,/end/s/foo/bar/g' filename
#命令組合:從第1行開(kāi)始,到第一個(gè)空白行為止,替換"foo"為"bar"
sed '1,/^$/s/foo/bar/g' filename
#倒數(shù)行范圍:從第1行開(kāi)始,直到包含"end"的行之前,替換"foo"為"bar"
sed '1,/end/!s/foo/bar/g' filename
#指定行的倍數(shù):替換所有奇數(shù)行中的"foo"為"bar"
sed '1~2s/foo/bar/g' filename
更新操作(a\i\c\)
a,i,c\ 分別表示在行下追加、行上插入和整行更新,字母符合后面 \ 可以省略
2.4 行下追加(a\)
#將 "this is a test line" 追加到含有 "hello" 行的下面
sed -i '/hello/a\this is a test line' filename
#在第 2 行之后插入 "this is a test line"
sed -i '2a\this is a test line' filename
2.5 行上插入(i\)
#將 "this is a test line" 插入到含有 "librarookie" 的行上面
sed -i '/librarookie/i\this is a test line' filename
#在第 5 行之前插入 "this is a test line"
sed -i '5i\this is a test line' filename
2.6 替換當(dāng)前行(c\)
#將含有 "librarookie" 的行變成 "this is a test line"
sed -i '/librarookie/c\this is a test line' filename
#將第 5 行變成 "this is a test line"
sed -i '3c\this is a test line' filename
2.7 刪除操作(d)
#刪除全文
sed -i 'd' filename
#刪除第 2 行
sed -i '2d' filename
#刪除最后一行
sed -i '$d' filename
#刪除空白行
sed -i '/^$/d' filename
#以 # 號(hào)開(kāi)頭的行(刪除注釋)
sed -i '/^#/d' filename
#刪除文件中所有開(kāi)頭是 test的行
sed -i '/^test/d' filename
#刪除文件的第 2 行到 末尾所有行
sed -i '2,$d' filename
2.8 腳本文件(-f)
sed腳本 scriptfile 是一個(gè)sed的命令清單,啟動(dòng)Sed時(shí),以 -f 選項(xiàng)引導(dǎo)腳本文件名。Sed腳本規(guī)則如下:
- 在命令的末尾不能有任何空白或文本;
- 如果在一行中有多個(gè)命令,要用分號(hào)分隔;
- 以 # 開(kāi)頭的行為注釋行,且不能跨行;
#這將按照 scriptfile.sed 文件中的命令來(lái)編輯 filename 文件
sed -f scriptfile.sed filename
#如果你不希望實(shí)際修改文件,可以添加 -n 標(biāo)志和 p命令來(lái)打印結(jié)果而不是直接寫入文件
sed -nf scriptfile.sed filename
scriptfile.sed 內(nèi)容如下:
# 這是注釋,會(huì)被sed忽略
# 替換文件中的第一行中的"old_string"為"new_string"
1s/old_string/new_string/
# 從第3行到第5行,替換"foo"為"bar"
3,5s/foo/bar/g
# 刪除包含"delete_this_line"的行
/delete_this_line/d
# 在包含"insert_before"的行之前插入新行
/insert_before/i\
This is the new line to insert
# 在文件末尾添加一行
$ a\
This is the new line at the end of the file
2.9 標(biāo)記操作,已匹配字符串標(biāo)記(&)
符合匹配條件字符串的標(biāo)記,可以理解為匹配結(jié)果的變量名
#正則表達(dá)式 "\w\+" 匹配每一個(gè)單詞,使用 [&] 替換它,& 對(duì)應(yīng)于之前所匹配到的單詞:
echo this is a test line | sed 's/\w\+/[&]/g'
[this] [is] [a] [test] [line]
# 將 hello 替換為 helloworld
sed 's/hello/&world/' filename
helloworld
2.10 子串匹配標(biāo)記(\1)
匹配給定樣式的其中一部分:
- (..) 用于匹配子串,對(duì)于匹配到的第一個(gè)子串就標(biāo)記為 \1,依此類推匹配到的第二個(gè)結(jié)果就是 \2;
- 未定義 (..) 時(shí),效果等同于 & 標(biāo)記;
#echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
this is 7 in a number
#命令中 digit 7,被替換成了 7。樣式匹配到的子串是 7。
#\(..\) 用于匹配子串。[a-z]+ 匹配小寫字母,為第一個(gè)子串\1;[A-Z]+ 匹配大寫字母,為第二個(gè)子串\2
echo "world HELLO" | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
HELLO world
# HELLO 被標(biāo)記為 \1,將
echo "hello world" | sed 's/\(hello\) world/\1 librarookie/'
hello librarookie
2.11 組合多個(gè)表達(dá)式(-e)
#1. 使用 -e 選項(xiàng),指定多個(gè) sed 表達(dá)式
sed -e '表達(dá)式' -e '表達(dá)式' filename
#2. 使用管道符 | ,對(duì)結(jié)果重復(fù)使用 sed 命令
sed '表達(dá)式' | sed '表達(dá)式' filename
#1. 在表達(dá)式中使用 ;
sed '表達(dá)式; 表達(dá)式' filename
2.12 文件讀寫(r/w)
#1. 從文件讀?。╮)
#將文件 datafile 里的內(nèi)容讀取出來(lái),顯示在文件 filename 中匹配 test 的行下面
#如果匹配多行,則將文件 datafile 的內(nèi)容,顯示在文件 filename 中所有匹配 test 的行下面
sed -i '/test/r datafile' filename
#2. 寫入新文件(w)
#將文件 filename 中所有匹配 test 的數(shù)據(jù)行,都寫入新文件 newfile 里面
sed -n '/test/w newfile' filename
#將替換后的 filename 寫入 newfile
sed 's/foo/bar/w newfile' filename
2.13 保持和獲?。╤/H/g/G)
#將任何包含 test 的行都被復(fù)制并追加到該文件的末尾
sed -e '/test/h' -e '$G' file
# -e '/test/h'
#1. 匹配 test 的行被找到后,將存入模式空間,
#2. h 命令將其復(fù)制并存入保留空間(保持緩存區(qū)的特殊緩沖區(qū)內(nèi))。
#
# -e '$G'
#1. 當(dāng)?shù)竭_(dá)最后一行($)后,
#2. G 命令取出保留空間的行,然后把它放回模式空間中,且追加到現(xiàn)在已經(jīng)存在于模式空間中的行的末尾。
#3. 在這個(gè)例子中就是追加到最后一行。
2.14 保持和互換(h/x)
互換模式空間和保持緩沖區(qū)的內(nèi)容。也就是把包含test與check的行互換:
sed -e '/test/h' -e '/check/x' file
2.15 打印奇數(shù)行或偶數(shù)行
#方法1:利用 n ,打印一行,隱藏一行
sed -n 'p;n' test.txt #奇數(shù)行
sed -n 'n;p' test.txt #偶數(shù)行
#方法2:利用步長(zhǎng) 2 ,跳過(guò)非目標(biāo)行
sed -n '1~2p' test.txt #奇數(shù)行
sed -n '2~2p' test.txt #偶數(shù)行
2.16 退出命令(q)
#打印完第10行后,退出sed
sed '10q' filename