1. 基本概念

sed是一个流式编辑器程序,它读取输入流(可以是文件、标准输入)的每一行放进模式空间(pattern space) , 同时将此行行号通过sed行号计数器记录在内存中,然后对模式空间中的行进行模式匹配,如果能匹配上则使用sed程序内部的命令进行处理,处理结束后,从模式空间中输出(默认)出去,并清空模式空间,随后再从输入流中读取下一行到模式空间中进行相同的操作,直到输入流中的所有行都处理完成。由此可见,sed是一个循环一个循环处理内容的

每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间Pattern Space)。

这是sed的一个循环的过程 :

  1. 读取输入流的一行到模式空间。

  2. 对模式空间中的内容进行匹配和处理。

  3. 自动输出模式空间内容。

  4. 清空模式空间内容。

  5. 读取输入流的下一行到模式空间。

(注 : 如果是读取文件数据 , 则会每次需要的时候一次性加载一定量(比如多行)的数据到os buffer , 然后sedos buffer中一行一行读取 , 并不是要读一行就从磁盘文件中加载一行。另外 , 如果是管道或其它输入流 , 则直接从对应的缓存中一行一行读取。验证命令 : sed ‘p;s/.*/:>filename/e;d’ filename)

上述整个循环过程中 , 第2步是我们写sed命令所修改的地方 , 其余的几个步骤 , 通过命令行无法改变。但是 , sed有几个命令和选项能改变第3、4步的行为 , 使其输出总是输出空内容或无法清空模式空间。

一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。如果使用vi命令打开几十M上百M的文件,明显会出现有卡顿的现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开。Sed就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快。

2. sed的基本用法

sed语法格式 : sed OPTIONS SCRIPT INPUT_STREAM

其中SCRIPT部分就是所谓的sed脚本,它是sed内部命令的集合 , sed中的命令有些奇特,它包含行匹配以及要执行的命令。格式为ADDR1[,ADDR2]cmd_list。例如,要对第2行执行删除命令 , 其命令为sed 2d filename , 只输出第4行到6行 , 其命令为sed -n 4,6p

  • 常用选项(OPTIONS)

    • -n : 不输出模式空间内容到屏幕 , 即不自动打印

    • -e : 多点编辑

    • -f /PATH/SCRIPT_FILE : 从指定文件中读取编辑脚本

    • -r, -E : 使用扩展正则表达式

    • -i.bak : 该选项指定要将sed的输出结果保存(覆盖的方式)到当前编辑的文件中。.bak是由自己随机定义的。原理当指定了.bak之后,在运行命令之前,会将需要修改的文件提前以·bak结尾,备份一份。

2.1 SCRIPT

sedscript主要由两个部分组成 : 1. 地址定界;2. 行编辑命令。 且地址定界需要写在行操作命令之前,需要先确定操作的行,才能进行接下来的操作。

说明下sed命令行SCRIPT部分的写法比较灵活 , 大致有以下几种:

# 一行式。多个命令使用分号分隔
sed Address{cmd1;cmd2;cmd3...}
​
# 多个表达式时 , 可以使用"-e"选项 , 也可以不用 , 但使用分号分隔
sed Address1{cmd1;cmd2;cmd3};Address2{cmd1;cmd2;cmd3}...
sed  -e 'Address1{cmd1;cmd2;cmd3}' -e 'Address2{cmd1;cmd2;cmd3}' ...
​
# 分行写时
sed Address1{
    cmd1
    cmd2
    cmd3
}
Address2{
    cmd1
    cmd2
    cmd3
}

如果是写在文件中 , 即sed脚本 , 以文件名为a.sed为例。

#!/usr/bin/sed -f
#注释行
Address1{cmd1;cmd2...}
Address2{cmd1;cmd2...}
......

其中cmd部分还可以进行模式匹配,也即类似于Address{{pattern1}cmd1;{pattern2}cmd2}的写法。例如 , /^abc/{2d;p}

2.1.1 地址定界

sed将输入流中的行读取到模式空间后,就需要对模式空间中的内容进行匹配 , 如果能匹配就能执行对应的命令 , 如果不能匹配就直接输出、清空模式空间并进入下一个sed循环读取下一行。

匹配的过程称为定址。定址表达式有多种,但总的来说,其格式为[ADDR1][,ADDR2]。这可以分为3种方式 :

  1. ADDR1ADDR2都省略时,表示所有行都能被匹配上。

  2. 省略ADDR2时 , 表示只有被ADDR1表达式匹配上的行才符合条件。

  3. 不省略ADDR2时,是范围地址。表示从ADDR1匹配成功的行开始 , 到ADDR2匹配成功的行结束。

无论是ADDR1还是ADDR2,都可以使用两种方式进行匹配 : 行号正则表达式

这种地址定界一般有如下几种表示方式:

  • 单地址(只给出ADDR1地址)

    • # : 匹配第#行。

    • /pattern/ : 被此处模式所能够匹配到的每一行,这里的pattern匹配便是直接按照正则表达式的方式匹配。

    • : 匹配最后一行。如果指定了-i或-s,则匹配的是每个文件的最后一行。总之,匹配的是每个输入流的最后一行。

sed采用行号计数器来临时记录当前行的行号 , 因此sed在读取到最后一行前即使是倒数第二行的时候 , 完全不知道最后一行是第几行 , 所以代表最后一行的$无法进行任何数学运算 , 例如倒数第二行使用$-1表示是错误的。而且 ,只是一个额外的标记符号 , 当sed读取到输入流的最后一行时 , 发现这就是最后一行 , 于是为此行打上记号 , 并读取到模式空间中。

  • 地址范围

    • #,# : 从#行到第#行 , 3 , 6 从第3行到第6

    • #,+# : 从#行到+#行,3,+4表示从3行到第7

    • /pat1/,/pat2/ : 从能匹配到的/pat1/模式的行开始,一直到匹配/pat2/模式行结束

    • #,/pat2//pat1/,# : 从#行到匹配的/pat2/行或者从/pat1/匹配到的行到#行。

  • 步长符号(~) : n1~n2,表示从第n1行开始 , 每隔n2行匹配一次 , 一直到最后一行

    • 1~2 : 奇数行

    • 2~2 : 偶数行

    • ~ 符号还有一个特别的用法,n1,~n2 : 表示从n1行开始一直匹配到n2的倍数行结束

[root@localhost tmp]# seq 1 10 | sed -n '5,~3p'  #这里6是3的倍数
5
6
​
[root@node02 ~]# seq 1 100 | sed -n '4,~4p'  #这里8是4的倍数
4
5
6
7
8
[root@node02 ~]# seq 1 100 | sed -n '4,~2p'  #6是2的倍数
4
5
6
​
[root@localhost tmp]# seq 1 10 | sed -n '4,~3p' 
4
5
6
[root@localhost tmp]# seq 1 10 | sed -n '1,+3p'
1
2
3
4

2.1.2 行编辑命令

  • d: 删除模式空间匹配的行 , 并立即启用下一轮循环。删除整个模式空间中的内容 , 并立即退出当前SCRIPT循环 , 进入下一个sed循环 , 即读取下一行

  • p : 打印当前模式空间内容 , 追加到默认输出之后

  • a [\]text : 在指定行后面追加文本 支持使用\n实现多行追加

  • i [\]text : 在行前面插入文本

  • c [\]text : 替换行为单行或多行文本

  • w /path/somefile : 保存模式匹配的行至指定文件

  • r /path/somefile : 读取指定文件的文本至模式空间中 匹配到的行后

  • = : 为模式空间中的行打印行号,由于值是存在于内存中,而非模式空间中 , 因此不受-n选项的影响。依赖于输出流的命令 , 只要有输出动作就会追加在该输出流的尾部

  • & : 它会在模式匹配成功后 , 将匹配成功的行根据模式进行分割 , 这样就存在三个部分的内容 : 模式之前的内容 , 模式匹配的内容 , 模式之后的内容。然后新的输出中 , 先输出 模式之前的内容 , 再输出 要替换的语句,并把要替换的语句中的 & 换成 模式匹配的内容 , 最后输出 模式之后的内容

  • y : 该命令和"tr"命令的映射功能一样 , 都是将字符进行一一替换。

  • ! :模式空间中匹配行取反处理

  • q : 执行后还会使用自动输出动作输出模式空间的内容,之后立即退出

  • Q : 立即退出,不会输出模式空间内容。

  • s/// : 查找替换,支持使用其它分隔符,s@@@,s###。需要注意的是这里使用的替换模式和vim中替换模式使用方式一样

    • 替换修饰符的使用

    • g : 行内全局替换,替换行中所有匹配内容

    • p : 显示替换成功的行

    • w /PATH/FILE : 将替换成功的行保存至文件中

    • I,i : 忽略大小写

注:

  1. 需要注意的是sed共有两种输出流,第一种是模式空间中的输出流 ; 另外一种便是自动的输出动作形成的输出流 , 这种自动输出动作形成的输出流受-n选项的影响。sed中默认会先输出模式空间中的输出流,之后再输出动作形成的输出流。所以=输出的行号会添加再这两个输出流之间

  2. aic命令的TEXT部分写法是比较复杂的 , 如果TEXT只是几个简单字符 , 直接使用即可。但如果要TEXT是分行文本 , 或者包含了引号 , 或者这几个命令是写在{}中的 , 则普通的写法就无法实现,主要是由于这些命令符号之后如果直接使用的话会直接将这些符号后面的所有字符全部都认为是需要添加的内容,导致之后的}无法正常识别。需要使用符号\来转义行尾符号 , 通过这个符号开启一个新行 , 此后输入的内容都是TEXT , 直到遇到引号或者";"开头的行时。

范例

#p命令会将模式空间中的内容打印出来,之后会在该输出流中
[root@localhost tmp]# sed  -n 'p;=' 1.sh
#!/bin/bash
1
​
#由这个命令可以知道两种输出流的输出动作以及=命令生成的行号,顺序如下: 模式空间中的输出流;=形成的行号;自动输出动作形成的输出流
[root@localhost tmp]# echo -e "abc\ndef\nxyz" | sed '/abc/{p;=}' 
abc
1
abc
def
xyz
​
#将a.txt中包含大写字母的YES、Yes等替换成小写的yes。
sed 'y/YES/yes/' a.txt
​
#下列命令中,如果使用q会先输出对应模式空间中的内容,而Q会直接退出模式空间,同时a命令后面的\是必须带上的,表示开启一个新行。 
[root@localhost tmp]# echo -e 'abc\nxyz\ndef' | sed '/abc/{a \
123
;}'
abc
123
xyz
def
[root@localhost tmp]# echo -e 'abc\nxyz\ndef' | sed '/abc/{a\
123
;q}'
abc
123
[root@localhost tmp]# echo -e 'abc\nxyz\ndef' | sed '/abc/{a\
123
;Q}'
​
#使用命令之后,直接输入内容之后可以直接输出处理对应行
[root@centos8 ~]#sed ''
welcome
welcome
to
to
magedu
magedu
​
#功能类似cat功能,直接输出对应文件内的内容
[root@centos8 ~]#sed '' /etc/issue
\S
Kernel \r on an \m
​
#sed默认会输出对应输入的内容,同时p命令同样会输出对应输入内容,所以对应行会输出两次
[root@centos8 ~]#sed 'p' /etc/issue
\S
\S
Kernel \r on an \m
Kernel \r on an \m
​
# 使用-n命令之后,会禁用sed默认的输出功能
[root@centos8 ~]#sed -n '' /etc/issue
​
#此时由于禁用的sed默认的输出功能之后,只有p命令生效。直接打印一遍对应行即可
[root@centos8 ~]#sed -n 'p' /etc/issue
\S
Kernel \r on an \m、
​
#默认只打印匹配的第一行
[root@centos8 ~]#sed -n '1p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
​
#第二行重复输出两次
[root@centos8 ~]#ifconfig eth0 | sed '2p'
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255
inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255
inet6 fe80::20c:29ff:fe45:a8a1 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:45:a8:a1 txqueuelen 1000 (Ethernet)
RX packets 89815 bytes 69267453 (66.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 115634 bytes 79827662 (76.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
​
#只输出匹配的第二行
[root@centos8 ~]#ifconfig eth0 | sed -n '2p'
inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255
​
#只打印最后一行
[root@centos8 ~]#sed -n '$p' /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
​
#打印匹配到netmask对应的行
[root@centos8 ~]#ifconfig eth0 |sed -n '/netmask/p'
inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255
​
#打印出匹配到/dev/sd的行
[root@centos8 ~]#df | sed -n '/^\/dev\/sd/p'
/dev/sda2 104806400 4872956 99933444 5% /
/dev/sda3 52403200 398860 52004340 1% /data
/dev/sda1 999320 848568 81940 92% /boot
​
#打印3至6行
[root@centos8 ~]#seq 10 | sed -n '3,6p'
3
4
5
6
​
#打印3至3+4的行即3至7行
[root@centos8 ~]#seq 10 | sed -n '3,+4p'
3
4
5
6
7
​
#打印第3至最后一行
[root@centos8 ~]#seq 8 | sed -n '3,$p'
3
4
5
6
7
8
​
#打印所有的奇数行
[root@centos8 ~]#seq 10 |sed -n '1~2p'
1
3
5
7
9
​
#打印所有偶数行
[root@centos8 ~]#seq 10 |sed -n '2~2p'
2
4
6
8
10
​
#删除所有匹配到的奇数行,即只显示偶数行
[root@centos8 ~]#seq 10 |sed '1~2d'
2
4
6
8
10
​
#同时删除第二行和第四行
[root@centos8 ~]#sed -e '2d' -e '4d' seq.log
1
3
5
6
7
8
9
10
​
#删除第二行之后再删除第四行
[root@centos8 ~]#sed '2d;4d' seq.log
1
3
5
6
7
8
9
10
​
#直接修改seq.log文件中的内容,同时将原始文件备份为sql.log.orig
[root@centos8 ~]#seq 10 > seq.log
[root@centos8 ~]#sed -i.orig '2d;4d' seq.log
[root@centos8 ~]#cat seq.log
1
3
5
6
7
8
9
10
[root@centos8 ~]#cat seq.log.orig
1
2
3
4
5
6
7
8
9
10
​
#在文件/etc/httpd/conf/httpd.conf中的listen 9527 匹配到行之后添加 listen 80 \nlisten 8080内容
[root@centos8 ~]#sed -i '/^listen 9527/a listen 80 \nlisten 8080'
/etc/httpd/conf/httpd.conf
​
#删除所有以#开头的行
[root@centos8 ~]#sed -i '/^#/d' fstab
​
#只显示非#开头的行
[root@centos8 ~]#sed -n '/^#/!p' fstab
​
#修改网卡配置
[root@centos8 ~]#sed -Ei.bak '/^GRUB_CMDLINE_LINUX/s/(.*)(")$/\1 net.ifnames=0\2/' /etc/default/grub

范例 :

sed '2p' /etc/passwd
sed -n '2p' /etc/passwd
sed -n '1,4p' /etc/passwd
sed -n '/root/p' /etc/passwd
sed -n '2,/root/p' /etc/passwd 从2行开始
sed -n '/^$/=' file 显示空行行号
sed -n -e '/^$/p' -e '/^$/=' file
Sed'/root/a\superman' /etc/passwd行后
sed '/root/i\superman' /etc/passwd 行前
sed '/root/c\superman' /etc/passwd 代替行
sed '/^$/d' file
sed '1,10d' file
nl /etc/passwd | sed '2,5d'
nl /etc/passwd | sed '2a tea'
sed 's/test/mytest/g' example
sed -n 's/root/&superman/p' /etc/passwd  在匹配到的root后直接添加superman , 这里直接用&代替前面匹配的root
sed -n 's/root/superman&/p' /etc/passwd  在匹配的root前直接添加superman, 这里直接用&代替前面匹配的root
sed -e 's/dog/cat/' -e 's/hi/lo/' pets
sed -i.bak 's/dog/cat/g' pets

范例 : 取IP地址

[root@centos8 ~]#ifconfig eth0 |sed -nr "2s/[^0-9]+([0-9.]+).*/\1/p"
10.0.0.8
[root@centos8 ~]#ifconfig eth0 | sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'
10.0.0.8
[root@centos8 ~]#ifconfig eth0 | sed -n '2s/^.*inet //p' | sed -n 's/netmask.*//p'
10.0.0.8
[root@centos8 ~]#ifconfig eth0 | sed -n '2s/^.*inet //;s/ netmask.*//p'
10.0.0.8
[root@centos8 ~]#ifconfig eth0 | sed -rn '2s/(.*inet )([0-9].*)(netmask.*)/\2/p'
10.0.0.8

范例 : 取基名和目录名

echo "/etc/sysconfig/network-scripts/" |sed -r 's#(^/.*/)([^/]+/?)#\2#' 取基名
echo "/etc/sysconfig/network-scripts/" |sed -r 's#(^/.*/)([^/]+/?)#\1#' 取目录
​
#取目录名
[root@centos8 ~]#echo /etc/sysconfig/ | sed -rn 's#(.*)/([^/]+)/?#\1#p'
/etc
​
#取基名
[root@centos8 ~]#echo /etc/sysconfig/ | sed -rn 's#(.*)/([^/]+)/?#\2#p'
sysconfig

范例 :将非#开头的行加#

[root@centos8 ~]#sed -rn "s/^[^#]/#&/p" /etc/fstab
#UUID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0
#UUID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2
#UUID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0
#UUID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0
​
[root@centos8 ~]#sed -rn 's/^[^#](.*)/#\1/p' /etc/fstab
#UID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0
#UID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2
#UID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0
#UID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0
​
[root@centos8 ~]#sed -rn '/^#/!s@^@#@p' /etc/fstab
#
#UUID=1b950ef9-7142-46bd-975c-c4ac1e0d47e8 / xfs defaults 0 0
#UUID=667a4c81-8b4b-4a39-a111-b11cb6d09309 /boot ext4 defaults 1 2
#UUID=38d14714-c018-41d5-922c-49e415decbca /data xfs defaults 0 0
#UUID=a0efb2bb-8227-4317-a79d-0a70d515046c swap swap defaults 0 0

范例 : 将#开头的行删除#

[root@centos8 ~]#sed -ri.bak '/^#/s/^#//' /etc/fstab

范例 : 取分区利用率

[root@centos8 ~]#df | sed -nr '/^\/dev\/sd/s# .* ([0-9]+)%.*# \1#p'
/dev/sda2 3
/dev/sda5 1
/dev/sda1 14

范例 : 修改内核参数

[root@centos8 ~]#sed -nr '/^GRUB_CMDLINE_LINUX/s/"$/ net.ifnames=0"/p' /etc/default/grub
GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=8363289d-138e-4e4a-abaf-6e028babc924 rhgb quiet net.ifnames=0"
[root@centos8 ~]#sed -rn '/^GRUB_CMDLINE_LINUX=/s@(.*)"$@\1 net.ifnames=0"@p' /etc/default/grub
GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=a0efb2bb-8227-4317-a79d-0a70d515046c rhgb quiet net.ifnames=0"
[root@centos8 ~]#sed -rn '/^GRUB_CMDLINE_LINUX=/s@"$@ net.ifnames=0"@p' /etc/default/grub
GRUB_CMDLINE_LINUX="crashkernel=auto resume=UUID=a0efb2bb-8227-4317-a79d-0a70d515046c rhgb quiet net.ifnames=0 net.ifnames=0"

范例 : 修改网卡名称

[root@centos8 ~]#sed -ri '/^GRUB_CMDLINE_LINUX=/s@"$@ net.ifnames=0"@' /etc/default/grub
#centos7,8
[root@centos8 ~]#grub2-mkconfig -o /boot/grub2/grub.cfg
#ubuntu
[root@ubuntu ~]#grub-mkconfig -o /boot/grub/grub.cfg

范例 : 查看配置文件

sed -r '/^(#|$)/d' /etc/httpd/conf/httpd.conf
sed -r '/^#|^$/d' /etc/httpd/conf/httpd.conf

范例 : 引用变量,如果需要引用SHELL中的变量,需要在sed引用的过程中添加''' , 三个引用符号。或者在SCRIPT中直接使用双引号

[root@centos8 ~]#echo|sed "s/^/$RANDOM.rmvb/"
5242.rmvb
[root@centos8 ~]#echo|sed 's/^/$RANDOM.rmvb/'
$RANDOM.rmvb
[root@centos8 ~]#echo|sed 's/^/'''$RANDOM'''.rmvb/'

范例 :修改配置文件

[root@centos6 ~]#sed -e '/^#<VirtualHost/,/^#<\/VirtualHost>/s@#@@' -e '/^#NameVirtualHost/s@#@@' /etc/httpd/conf/httpd.conf

3. 高级引用

sed 中除了模式空间 , 还另外还支持保持空间Hold Space),利用此空间,可以将模式空间中的数据,临时保存至保持空间,从而后续接着处理,实现更为强大的功能。 需要注意的是,所有输入流都会先输入到模式空间中。

常用高级命令

  • P : 打印模式空间开端至\n内容,并追加到默认输出之前。输出模式空间中第一个换行符"\n"之前的内容。

  • n : 读取匹配到的行的下一行覆盖至模式空间。在sed的循环过程中,每个sed循环的第一步都是读取输入流的下一行到模式空间中 , 这是我们无法控制的动作。但sed有读取下一行的命令n。由于是读取下一行 , 所以它会触发自动输出的动作 , 于是就有了输出流。不仅如此 , 还应该记住的是 : 只要有读取下一行的行为 , 在其真正开始读取之前一定有隐式自动输出的行为。但需注意 , 当没有下一行可供n读取时(例如文件的最后一行已经被读取过了) , 将输出模式空间内容后直接退出sed程序 , 使得"n"命令后的所有命令都不会执行 , 即使是那两个隐含动作。

  • N : 读取匹配到的行的下一行追加至模式空间 , 相当于一次读取两行到模式空间。N命令在读取下一行前,虽然也有自动输出清空模式空间的动作 , 但该命令会把当前模式空间的内容锁住 , 使得自动输出的内容为空 , 也无法清空模式空间 , 然后读取下一行追加到当前模式空间中的尾部。追加时 , 原有内容和新读取内容使用换行符"\n"分隔 , 这样在模式空间中就实现了多行数据。即所谓的"多行模式"。 另外 , 当无法读取到下一行时(到了文件尾部) , 将直接退出sed程序 , 使得"N"命令后的命令不会再执行 , 这和"n"命令是一样的。

  • h : 把模式空间中的内容覆盖至保持空间中

  • H : 在保持空间的尾部加上一个换行符\n , 并将当前模式空间的内容追加到保持空间的尾部。

  • g : 从保持空间取出数据覆盖至模式空间

  • G : 在模式空间的尾部加上一个换行符\n , 并将当前保持空间的内容追加到模式空间的尾部。

  • x : 把模式空间中的内容与保持空间中的内容进行互换

  • d : 删除模式空间中的行。如果"d"后面还有命令 , 在删除模式空间后 , 这些命令不会执行 , 因为会立即退出当前SCRIPT循环。

  • D : 如果模式空间包含换行符 , 则删除直到第一个换行符的模式空间中的文本 , 并不会读取新的输 入行 , 而是在新的模式空间重新启动循环。如果模式空间不包含换行符 , 则会像发出d命令那样 重新读取新的输入流,并正常进行新的循环。主要用于删除多行模式空间中的所有行。也就是说"D"命令后的命令不会被执行。

范例 :

#当匹配到abc这一行之后直接运行n命令会触发自动输出动作,将模式空间的内容输出并清空,之后读取下一行。因此运行=之后会直接输出第二行行号,之后运行p命令输出第二行中模式空间内容,再出发自动输出动作,所以def输出两行
[root@localhost tmp]# echo -e "abc\ndef\nxyz" | sed '/abc/{n;=;p}' 
abc
2
def
def
xyz
​
#d命令之后就会直接退出当前script循环
[root@localhost tmp]# echo -e 'abc\nxyz' | sed '{/abc/d;=}'
2
xyz
​
[root@localhost tmp]# seq 1 10 | sed 'N;s/\n/ /'
1 2
3 4
5 6
7 8
9 10
​
sed -n 'n;p' FILE  #显示偶数行
sed '1!G;h;$!d' FILE #逆向显示文件内容
sed 'N;D' FILE    #取出文件最后一行
seq 10 |sed '3h;9G;9!d'
sed '$!N;$!D' FILE #去除最后两行
sed '$!d' FILE #去除最后一行
sed 'G' FILE # 在每一行后面加一个空白行
sed 'g' FILE # 将所有匹配的行全部用空白行替换,直接显示为多行空白行
sed '/^$/{d;G}' FILE #合并多行空白行
sed 'n;d' FILE #显示奇数行
sed -n '1!G;h;$p' FILE #逆向显示文件内容

参考链接

金马俊龙 : 博客园 : sed修炼系列(一) : 花拳绣腿之入门篇

熊熊