sed 是一個比較古老的,功能十分強大的用於文本處理的流編輯器,加上正則表達式的支持,可以進行大量的復雜的文本編輯操作。sed 本身是一個非常復雜的工具,有專門的書籍講解 sed 的具體用法,但是個人覺得沒有必要去學習它的每個細節,那樣沒有特別大的實際意義。我們經常在 Dockerfile 文件中看到 sed -i 之類的語句,故此研究了解一些 sed 命令。
一、sed 簡介
1、sed 是什么
sed 全名為 stream editor,流編輯器,用程序的方式來編輯文本,功能相當的強大,可以在大多數操作系統中使用。
sed 的出現作為 grep 的繼任者,與vim等編輯器不同,sed 是一種非交互式編輯器(即用戶不必參與編輯過程),它使用預先設定好的編輯指令對輸入的文本進行編輯,完成之后再輸出編輯結構。sed 基本上就是在玩正則模式匹配,所以,玩sed的人,正則表達式一般都比較強。
2、sed 工作原理
sed 會一次處理一行內容。處理時,把當前處理的行存儲在臨時緩沖區中,成為"模式空間",接着用 sed 命令處理緩沖區中的內容,處理完成后,把緩沖區的內容送往屏幕。接着處理下一行,這樣不斷重復,直到文件末尾。文件內容並沒有改變,除非你使用重定向存儲輸出。
3、正則表達式的匹配過程
簡單描述一下正則表達式的匹配過程,就是拿正則表達式所表示的字符串去和原文字符串內容去匹配,直到匹配到原文內容字符串中的一個完整子串就表示匹配成功。舉個例子,有一行文件內容 "this is better desk",這里用"esk"去匹配,匹配過程是這樣的:首先拿e去匹配文件行內容,從this開始,直到better的e,第一個字符匹配成功,接着s去匹配better字符e后邊的t字符,沒有匹配成功;然后重新拿esk中的e去和better的第二個t去匹配,沒有成功,接着原始內容的下一個字符,直到desk中的e字符,逐個匹配s,k字符,到此為止,esk成功匹配,正則表達式匹配完畢。整個過程就是這樣,即使再復雜的正則表達式的匹配過程也是按照此過程來進行的。
二、sed 基本語法
# sed [-nefr] [動作] 選項與參數: -n :使用安靜(silent)模式。在一般 sed 的用法中,所有來自 STDIN 的數據一般都會被列出到終端上。但如果加上 -n 參數后,則只有經過sed 特殊處理的那一行(或者動作)才會被列出來。 -e :直接在命令列模式上進行 sed 的動作編輯; -f :直接將 sed 的動作寫在一個文件內, -f filename 則可以運行 filename 內的 sed 動作; -r :sed 的動作支持的是延伸型正規表示法的語法。(默認是基礎正規表示法語法) -i :直接修改讀取的文件內容,而不是輸出到終端。 動作說明: [n1[,n2]]function n1, n2 :不見得會存在,一般代表『選擇進行動作的行數』,舉例來說,如果我的動作是需要在 10 到 20 行之間進行的,則『 10,20[動作行為] 』 function: a :新增, a 的后面可以接字串,而這些字串會在新的一行出現(目前的下一行)~ c :取代, c 的后面可以接字串,這些字串可以取代 n1,n2 之間的行! d :刪除,因為是刪除啊,所以 d 后面通常不接任何咚咚; i :插入, i 的后面可以接字串,而這些字串會在新的一行出現(目前的上一行); p :列印,亦即將某個選擇的數據印出。通常 p 會與參數 sed -n 一起運行~ s :取代,可以直接進行取代的工作哩!通常這個 s 的動作可以搭配正規表示法!例如 1,20s/old/new/g 就是啦!
sed -i 就是直接對文本文件進行操作的
sed -i 's/原字符串/新字符串/' /home/1.txt sed -i 's/原字符串/新字符串/g' /home/1.txt
這兩條命令的區別就是和正則表達式一樣,加了 g 就是每行都全局替換。沒有 g 就是每行只替換第一個匹配的。比如:
#cat 1.txt d ddd #ff sed -i 's/d/7523/' /home/1.txt 執行結果 7523 7523dd #ff // 只替換每行的第一個
sed -i 's/d/7523/g' /home/1.txt 執行結果 7523
752375237523 #ff // 每行全部替換
其他命令例子
// 去掉 “行首” 帶“@”的首字母@
sed -i 's/^@//' file // 特定字符串的行前插入新行
sed -i '/特定字符串/i 新行字符串' file // 特定字符串的行后插入新行
sed -i '/特定字符串/a 新行字符串' file // 特定字符串的刪除
sed -i '/字符串/d' file
1、選項 -e:如果需要用sed對文本內容進行多種操作,則需要執行多條子命令來進行操作
// 例子1:
echo -e 'hello world' | sed -e 's/hello/A/' -e 's/world/B/'
// 結果:A B // 例子2:
echo -e 'hello world' | sed 's/hello/A/;s/world/B/'
// 結果:A B
說明:例子1和例子2的寫法的作用完全等同,可以根據喜好來選擇,如果需要的子命令操作比較多的時候,無論是選擇-e選項方式,還是選擇分號的方式,都會使命令顯得臃腫不堪,此時使用-f選項來指定腳本文件來執行各種操作會比較清晰明了。
2、選項-i:sed默認會把輸入行讀取到模式空間,簡單理解就是一個內存緩沖區,sed子命令處理的內容是模式空間中的內容,而非直接處理文件內容。因此在sed修改模式空間內容之后,並非直接寫入修改輸入文件,而是打印輸出到標准輸出。如果需要修改輸入文件,那么就可以指定-i選項。
// 例子1:不加 -i,原文件沒變,只是輸出
cat file.txt hello world [root@localhost]# sed 's/hello/A/' file.txt A world [root@localhost]# cat file.txt hello world // 例子2:加上 -i,不輸出,但是修改了原文件
[root@localhost]# sed -i 's/hello/A/' file.txt [root@localhost]# cat file.txt A world // 例子3:
[root@localhost]# sed –i.bak 's/hello/A/' file.txt
說明:最后一個例子會把修改內容保存到file.txt,同時會以file.txt.bak文件備份原來未修改文件內容,以確保原始文件內容安全性,防止錯誤操作而無法恢復原來內容。
3、選項-f:還記得 -e 選項可以來執行多個子命令操作,用分號分隔多個命令操作也是可以的,如果命令操作比較多的時候就會比較麻煩,這時候把多個子命令操作寫入腳本文件,然后使用 -f 選項來指定該腳本。
// 例子1:
echo "hello world" | sed -f sed.script // 結果:A B // sed.script腳本內容:
s/hello/A/ s/world/B/
// 說明:在腳本文件中的子命令串就不需要輸入單引號了。
4、選項-r:sed命令的匹配模式支持正則表達式的,默認只能支持基本正則表達式,如果需要支持擴展正則表達式,那么需要添加-r選項。
echo "hello world" | sed -r 's/(hello)|(world)/A/g'
// A A
三、數字定址和正則定址
1、關於定址的概念
默認情況下sed會對每一行內容進行匹配、處理、輸出,某些情況不需要對處理的文本全部編輯,只需要其中的一部分,比如1-10行,偶數行,或者是包含"hello"字符串的行,這種情況下就需要我們去定位特定的行來處理,而不是全部內容,這里把這個定位指定的行叫做"定址"。
2、數字定址
數字定址其實就是通過數字去指定具體要操作編輯的行,數字定址有幾種方式,每種方式都有不同的應用場景,下邊以舉例的方式來描述每種數字定址的用法。
// 例子1:將第4行中hello字符串替換為A,其它行如果有hello也不會被替換。
sed –n ‘4s/hello/A/’ message // 例子2:將第2-4行中hello字符串替換為A,其它行如果有hello也不會被替換。
sed –n ‘2,4s/hello/A/’ message // 例子3:從第2行開始,再接着往下數4行,也就是2-6行,這些行會把hello字符替換為A
sed –n ‘2,+4s/hello/A/’ message // 例子4:第4行開始,到第6行。解釋6的由來,"4,~3"表示從4行開始到下一個3的倍數,這里從4開始算,那就是6了,當然9就不是了,因為是要求3的第一個超過前邊數字4的倍數,感覺這種適用場景不會太多。
sed –n ‘4,~3s/hello/A/’ message // 例子5:從第4行開始,每隔3行就把hello替換為A。比如從4行開始,7行,10行等依次+3行。這個比較常用,比如3替換為2的時候,也就是每隔2行的步調,可以實現奇數和偶數行的操作。
sed –n ‘4~3s/hello/A/’ message // 例子6:$符號表示最后一行,和正則中的$符號類似,但是第1行不用^表示,直接1就行了。
sed –n ‘$s/hello/A/’ message // 例子7:!符號表示取反,該命令是將除了第1行,其它行hello替換為A,上述定址方式也可以使用!符號。
sed -n ‘1!s/hello/A/’ message
3、正則定址:正則定址使用目的和數字定址完全一樣,使用方式上有所不同,是通過正則表達式的匹配來確定需要處理編輯哪些行,其它行就不需要額外處理。
// 例子1:將匹配到nihao的行執行刪除操作。
sed -n ‘/nihao/d’ message // 例子2:刪除空行
sed -n ‘/^$/d’ message // 例子3:匹配以TS開頭的行到TE開頭的行之間的行,把匹配到的這些行刪除
sed -n ‘/^TS/,/^TE/d’ message
4、數字定址和正則定址混用
sed -n ‘1,/^TS/d’ message // 說明:匹配從第1行到TS開頭的行,把匹配的行刪除。
5、關於定址的分組命令
// 例子1:該命令表示將從TS開頭的行到TE開頭的行之間范圍的行內容中CN替換為China,並且把Beijing替換為BJ,類似於多命令之間用分號的那種方式,不過這樣定址代碼只寫了一遍,相當於執行了一條子命令。
/^TS/,/^TE/{ s/CN/China/ s/Beijing/BJ/ } // 例子2:效果類似例子1
sed -n ‘2,3s{/cn/china/;/a/b/}’ message
6、sed定址的總結
sed 默認的命令執行范圍是全局編輯的,如果不明確指定行的話,命令會在所有輸入行上執行。
如果想僅對其中部分行執行命令,可以使用地址限制。
如果給了 2 個地址,即地址對(地址范圍),則命令匹配的這個地址范圍內執行,但是需要注意的是:對於像 "addr1,addr2" 這種形式的地址匹配,如果addr1 匹配,則匹配成功,"開關"打開,在該行上執行命令,此時不管 addr2 是否匹配,即使 addr2 在 addr1 這一行之前;接下來讀入下一行,如果addr2 匹配,則執行命令,同樣開關"關閉";如果 addr2 在 addr1 之后,則一直處理到匹配為止,換句話說,如果 addr2 一直不匹配,則開關一直不關閉,因此會持續執行命令到最后一行。
四、基本子命令
1、子命令 a:表示在指定行下邊插入指定行的內容。
// 例子1:將message文件中每一行下邊都插入添加一行內容是A
sed ‘a A’ message // 例子2:將message文件中1-2行的下邊插入添加一行內容是A
sed ‘1,2a A’ message // 例子3:將message文件中1-2行的下邊分別添加3行,3行內容分別是A、B、C,這里使用了\n,插入多行內容都可以按照這種方式來實現。
sed ‘1,2a A\nB\nC’ message
這里有個應用實例:修改 ssh 的配置 —— sed -i '/#PermitRootLogin prohibit-password/a PermitRootLogin yes' /etc/ssh/sshd_config
2、子命令 i:和 a 使用上基本上一樣,只不過是在指定行上邊插入指定行的內容。
3、子命令 c:表示把指定的行內容替換為自己需要的行內容。
// 例子1:將message文件中所有的行內容都分別替換為A行內容。
sed ‘c A’ message // 例子2:將message文件中1-2行的內容替換為A // 注意這里說的是將1-2行所有的內容只替換為一個A內容,也就是1-2行內容編程了一行,定址如果連續就是這種情況。如果想把1-2行分別替換為A,可以參考下個例子的方式。
sed ‘1,2c A’ message // 例子3:將message中1-2行內容分別替換為了A,需要在替換內容上手動加換行\n,這樣當然也可以將一行內容替換為多行內容。
sed ‘1,2c A\nA’ message
4、子命令 d:表示刪除指定的行內容,比較簡單,更容易理解。
// 例子1:將message所有行全部刪除,因為沒有加定址表達式,所以平時如果需要刪除指定行內容,需要在子命令前加定址表達式。
sed ‘d’ message // 例子2:將message文件中1-3行內容刪除
sed ‘1,3d’ message
5、子命令 y:表示字符替換,可以替換多個字符,只能替換字符不能替換字符串,且不支持正則表達式,具體使用方法看例子。
// 把message中所有a字符替換為A符號,所有b字符替換為B符號
sed ‘y/ab/AB/’ message // 強調一下,這里的替換源字符個數和目的字符個數必須相等;字符不支持正則表達式;源字符和目標字符每個字符需要一一對應。
6、子命令 =:可以將行號打印出來。
sed ‘1,2=’ message // 結果
1 nihao 2 hello world 說明:將指定行的上邊顯示行號。
7、子命令 r:類似於a,也是將內容追加到指定行的后邊,只不過 r 是將指定文件內容讀取並追加到指定行下邊
sed ‘2r a.txt’ message // 將a.txt文件內容讀取並插入到message文件第2行的下邊。
五、子命令 s:為替換子命令,
是平時sed使用的最多的子命令,沒有之一。因為支持正則表達式,功能變得強大無比,下邊來詳細地說說子命令s的使用方法。
1、基本語法:[address]s/pattern/replacement/flags
s 字符串替換,替換的時候可以把 / 換成其它的符號,比如=。
// replacement部分用下列字符會有特殊含義:
>>> &:用正則表達式匹配的內容進行替換 >>> \n:回調參數 >>> \(\):保存被匹配的字符以備反向引用\n時使用,最多9個標簽,標簽書序從左到右 // Flags
>>> n:可以是1-512,表示第n次出現的情況進行替換 >>> g:全局更改 >>> p:打印模式空間的內容 >>> w file:寫入到一個文件file中
2、實例用法
// 測試文件:
# cat message hello 123 world // 例子1:將message每行包含的第一個hello的字符串替換為HELLO,這是最基本的用法
sed ‘s/hello/HELLO/’ message // 例子2:使用了擴展正則表達式,需要加-r選項
sed -r ‘s/[a-z]+ [0-9]+ [a-z]+/A/’ message // 替換結果:A // 例子3:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/\1\2\3/’ message //結果:hello 123 world //說明:再看下一個例子就明白了。 //例子4:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/\3\2\1/’ message //結果:world 123 hello //說明:\1表示正則第一個分組結果,\2表示正則匹配第二個分組結果,\3表示正則匹配第三個分組結果。 //例子5:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/&/’ message //結果:hello 123 world //說明:&表示正則表達式匹配的整個結果集。 //例子6:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/111&222/’ message //結果:111hello 123 world222 //說明:在匹配結果前后分別加了111、222。 //例子7:
sed -r ‘s/.*/111&222/’ message //說明:在message文件中每行的首尾分別加上111、222。 //例子8:
sed ‘s/i/A/g’ message //說明:把message文件中每行的所有i字符替換為A,默認不加g標記時只替換每行的第一個字符。 //例子9:
sed ‘s/i/A/2’ message //說明:把message文件中每行的第2個i字符替換為A。 //例子10:
sed -n ‘s/i/A/p’ message //說明:加-p標記會把被替換的行打印出來,再加上-n選項會關閉模式空間打印模式,因此該命令的效果就是只顯示被替換修改的行。 //例子11:
sed -n ‘s/i/A/w b.txt’ message //說明:把message文件中內容的每行第一個字符i替換為A,然后把修改內容另存為b.txt文件。 //例子12:
sed -n ‘s/i/A/i’ message //說明:把message文件中每一行的第一個i或I字符替換為A字符,也即是忽略大小寫
更多詳細用法可以看這篇文章:https://blog.csdn.net/qq_33468857/article/details/84324609