“文本三劍客”中,grep是文本過濾器,而sed是基于行的文本流編輯器。
sed是將文件中的文本逐行讀取到內存中進行處理。
- sed工作原理
- sed命令
2.1 OPTION
2.2 SCRIPT- sed使用示例
1 sed工作原理
sed有兩個工作空間,Pattern space(模式空間)和Hold space(保持空間)。
Pattern space 模式空間
sed會把文件內容的每一行復制一份出來放到自己的Pattern space中,在其中處理以后,處理的結果送到stdout(標準輸出):
- 默認情況sed會處理每一行,但我們可以讓sed處理只被模式(如正則表達式)匹配到的文本。
- Pattern space中被用戶給出的模式匹配到的內容,則edit(編輯)后再送到stdout。
- 如果不能匹配,則不做任何編輯操作,直接輸出到stdout。
Hold space 保持空間
Hold space用于在使用高級編輯功能時,實現與Pattern space空間中的內容進行追加、覆蓋、互換等操作。后邊高級編輯示例中會詳細解釋。
2 sed命令
sed - stream editor for filtering and transforming text
sed [OPTION]... 'SCRIPT' [input-file]...
sed命令需要將命令選項OPTION和處理腳本SCRIPT結合使用,來對文本進行特定的處理動作。
2.1 OPTION
- -n:不輸出模式空間的內容至屏幕stdout
[默認情況下,會將不被pattern匹配到的內容直接輸出到stdout,-n則不輸出這部分內容] - -e script,--expression=script:多點編輯;
默認只支持一個script(被pattern匹配到的內容將被執行的編輯命令),加-e可以指定多個script。
如:sed -e 's@^#[[:space:]]*@@' -e '/^UUID/d' /etc/fstab - -f /PATH/TO/SED_SCRIPT_FILE
SED_SCRIPT_FILE中,每行都有一個script,從文件中逐行讀取script,省去-e的繁瑣 - -r,--regexp-extended:支持擴展正則表達式
- -i[SUFFIX],--in-place[=SUFFIX]:直接編輯原文件(有風險,用之前先做備份)(sed默認不直接編輯原文件)。
2.2 SCRIPT
SCRIPT由"地址定界"和"編輯命令"兩部分組成,和vi編輯器的末行模式命令相似。兩部分中間無空格。
地址定界的語法格式
- 地址定界為空,表示全文;
- 單地址:
N:指定一個行號,表示第N行;
/PATTERN/:給出一個模式,表示被模式匹配到的每一行。 - 地址范圍:
M,N:表示從第M行到第N行;
M,+N:表示從第M行開始,到往后N行之間的所有行(包含第M行);
M,/PATTERN/:表示從第M行到被PATTERN匹配到的第一行之間的所有行
/PATTERN1/,/PATTERN2/:表示從被PATTERN1匹配到的第一行,到被PATTERN2匹配到的第一行之間的所有行。 - 用“~”表示步進:
如:1~2表示所有奇數行,2~2表示所有偶數行。
編輯命令
- d:delete pattern space,刪除模式匹配到的內容(模式空間中的內容)
d是一個特殊的操作 - p:print the current pattern space:打印模式匹配到的內容(模式空間中的內容)
p也是一個特殊的操作,會將pattern匹配到的內容顯示兩遍,如果不加選項,顯示結果為,模式空間中的全部內容+被pattern匹配到的內容;
如果只想顯示被pattern匹配的內容,配合使用-n選項,將不顯示默認輸出的模式空間全部內容。 - a \text:append,在模式匹配到的行后面追加文本“text”,支持使用\n(換行符號)實現多行追加(注意文本前用\轉義)
- i:insert,在模式匹配到的行前面插入文本“text”,支持使用\n(換行符號)實現多行插入(注意文本前用\轉義)
比如,在fstab文件中所有以UUID開頭的行前面加入注釋:
sed '/^UUID/i # Add device based on UUID.' fstab - c \text:把匹配到的行替換為"text"(注意是整行替換!!);
- w /PATH/TO/SOMEFILE:保存模式匹配到的行至指定的文件中;
例如:將fstab中所有以非#開頭的行保存到當前目錄下的fstab.new文件中:
sed '/[#]/w fstab.new' fstab - r /PATH/FROM/SOMEFILE:讀取指定文件的內容至當前文件被模式匹配到的行后面;
例如:將/etc/issue文件中的內容顯示到fstab文件第3行的后面:
sed '3r /etc/issue' fstab - =:為模式匹配到的行打印行號(在行的前面另起一個新行來打印行號);
例如:將fstab中所有以UUID開頭的行都打印行號:
sed '/^UUID/=' fstab - !:條件取反(!放在地址定界和編輯命令之間)
地址定界!編輯命令
例如:前邊有個例子,“將fstab中所有以非#開頭的行保存到當前目錄下的fstab.new文件中”
我們使用 “sed '/[#]/w fstab.new' fstab”;
在這里可以這樣“ sed '/^#/!w fstab.new' fstab”,它表示所有#號開頭的行都不寫入fstab.new文件,即非#開頭的行就寫入。 - s///:查找替換,其分隔符可自行指定,常用的有s@@@,s###等;
替換標記:
g:全局替換
w /PATH/TO/SOMEFILE:將替換成功的結果保存至指定文件中;
p:顯示替換成功的行;
高級編輯命令
高級編輯命令用于Pattern space和Hold space中內容的追加、覆蓋和互換,有以下幾個:
- Pttern space --> Hold space
h:把模式空間中的內容覆蓋至 > 保持空間(hold space)中;
H:把模式空間中的內容追加至 >> 保持空間(hold space)中; - Hold space --> Pattern space
g:把保持空間中的內容覆蓋至 > 模式空間中;
G:把保持空間中的內容追加至 >> 模式空間中; - 模式匹配的下一行 --> Pattern space
n:覆蓋讀取匹配到的行的下一行至 > 模式空間中
N:追加讀取匹配到的行的下一行至 >> 模式空間中 - Pattern space <--> Hold space
x:把模式空間中的內容與保持空間中的內容互換; - 刪除Pattern sapce中的行
d:刪除模式空間中的行
D:刪除多行模式空間中的所有行(多行模式空間比如,N命令追加到模式空間中行)
3 sed使用示例
普通編輯命令示例
sed是否用得好,主要是看SCRIPT的編寫能力。
- 刪除/etc/grub2.cfg文件中所有以空白字符開頭的行的行首的所有空白字符:
sed 's@^[[:space:]]\+@@' /etc/grub2.cfg
- 刪除/etc/fstab文件中所有以#開頭的行的行首的#號及#后邊的所有空白字符:
sed 's@^#[[:space:]]*@@' /etc/fstab
- 輸出一個絕對路徑給sed命令,要求取出其目錄名,類似dirname命令執行結果:
echo "/var/log/messages" | sed 's@[^/]\+/\?$@@'
或使用-r選項支持擴展正則表達式:
echo "/var/log/messages" | sed -r 's@[^/]+/?$@@'
高級編輯命令示例與命令解析
要使用sed的高級編輯命令,需要對sed的工作流程非常熟悉。
-
sed -n 'n;p' FILE
顯示偶數行(;分號用來分隔多個命令)
命令解析:
因為是n;p,沒有地址定界,表示逐行讀取所有內容。
1、首先讀取第1行到pattern space,遇到第一個命令是n,覆蓋讀取下一行(也就是第2行)到pattern space,所以現在pattern space中的內容變為第2行(第1行被覆蓋掉);
2、然后是p命令,將pattern space中的第2行顯示出來;
3、接著讀取第3行到pattern space,同上,顯示第4行;
4、以此類推,顯示所有偶數行。
注意:如果沒有-n選項,則將顯示全部內容,并且偶數行顯示兩遍。
sed '1!G;h;$!d' FILE
逆序顯示文件內容,相當于tac
命令解析:
1、讀取第1行到pattern space,先碰到“1!G”命令,表示第1行不做G操作;
2、然后碰到下一個命令“h”,表示將pattern space中的內容覆蓋至hold space,就是將第1行內容復制到了hold space中;
3、接下來碰到“$!d”命令,表示將pattern space中不是最后一行的內容全部刪除,pattern space中當前還有第1行的內容,則將其刪除。第1行的讀取到此結束。
4、接著讀取第2行,“1!G”命令將hold space中的內容(第1行的內容)覆蓋至pattern space,所以目前pattern space中的內容變為“第2行+第1行”;
5、然后碰到“h”命令,將pattern space中的內容(第2行+第1行)復制到hold space中(hold space中原來的第1行內容被覆蓋掉),所以現在hold space中的內容為“第2行+第1行”;
6、下一個命令“$!d”,同上,將pattern space中的“第2行+第1行”內容刪除。
第2行的讀取到此結束。
7、以此類推,知道讀取第n-1行結束時,hold space的內容為“第n-1行+第n-2行....第2行+第1行”,pattern space為空;
8、最后讀取第n行,“1!G”命令將hold space內容追加到pattern space,則pattern space內容變為“第n行...第1行”;
9、“h”命令將pattern space的內容覆蓋復制到hold space,則現在hold space的內容為“第n行...第1行”;
10、最后碰到“$!d”命令,因為是最后一行,則不刪除pattern space的內容,所以pattern space中的內容還是“第n行...第1行”。
11、讀取完全部內容后,默認輸出pattern space中的全部內容,即“第n行...第1行”,將原內容逆序顯示!sed '$!d' FILE
取出最后一行,相當于tail -1
命令解析:
1、讀取第1行到pattern space,pattern中的命令為“$!d”(不是最后一行,則刪除),所以把第1行從pattern space中刪了
2、依次類推,一直刪到倒數第2行;
3、到最后一行,不刪了,保留在pattern space中,然后輸出到stdout。其他幾個高級編輯命令示例
sed '$!N;$!D' FILE
:顯示最后兩行,相當于tail -2
sed '/^$/d;G' FILE
:刪除原有所有空白行,再為所有非空白行后添加一個空白行
sed 'n;d' FILE
:顯示奇數行;
sed 'G' FILE
:在原有的每行后方添加一個空白行;