“文本三剑客”中,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编辑器的末行模式命令相似。两部分中间无空格。
地址定界的语法格式
1. 地址定界为空,表示全文;
2. 单地址:
N:指定一个行号,表示第N行;
/PATTERN/:给出一个模式,表示被模式匹配到的每一行。
3. 地址范围:
M,N:表示从第M行到第N行;
M,+N:表示从第M行开始,到往后N行之间的所有行(包含第M行);
M,/PATTERN/:表示从第M行到被PATTERN匹配到的第一行之间的所有行
/PATTERN1/,/PATTERN2/:表示从被PATTERN1匹配到的第一行,到被PATTERN2匹配到的第一行之间的所有行。
4. 用“~”表示步进:
如: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 ” 命 令 , 表 示 将 p a t t e r n s p a c e 中 不 是 最 后 一 行 的 内 容 全 部 删 除 , p a t t e r n s p a c e 中 当 前 还 有 第 1 行 的 内 容 , 则 将 其 删 除 。 第 1 行 的 读 取 到 此 结 束 。 4 、 接 着 读 取 第 2 行 , “ 1 ! G ” 命 令 将 h o l d s p a c e 中 的 内 容 ( 第 1 行 的 内 容 ) 覆 盖 至 p a t t e r n s p a c e , 所 以 目 前 p a t t e r n s p a c e 中 的 内 容 变 为 “ 第 2 行 + 第 1 行 ” ; 5 、 然 后 碰 到 “ h ” 命 令 , 将 p a t t e r n s p a c e 中 的 内 容 ( 第 2 行 + 第 1 行 ) 复 制 到 h o l d s p a c e 中 ( h o l d s p a c e 中 原 来 的 第 1 行 内 容 被 覆 盖 掉 ) , 所 以 现 在 h o l d s p a c e 中 的 内 容 为 “ 第 2 行 + 第 1 行 ” ; 6 、 下 一 个 命 令 “ !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”命令,表示将patternspace中不是最后一行的内容全部删除,patternspace中当前还有第1行的内容,则将其删除。第1行的读取到此结束。4、接着读取第2行,“1!G”命令将holdspace中的内容(第1行的内容)覆盖至patternspace,所以目前patternspace中的内容变为“第2行+第1行”;5、然后碰到“h”命令,将patternspace中的内容(第2行+第1行)复制到holdspace中(holdspace中原来的第1行内容被覆盖掉),所以现在holdspace中的内容为“第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
:在原有的每行后方添加一个空白行;