介绍

sed是一个流编辑器,用于处理来自文件或管道的输入流,进行基本文本的匹配及处理。虽然在某些方面类似于允许进行脚本编辑(例如ed)的编辑器,但是sed仅仅对其输入内容从头到尾逐行读入、匹配和处理,因此效率更高。sed能够对来自于管道的文本进行过滤,与其他类型编辑器相比,这是它的独特之处。

GNU sed 一个流编辑器(一)

工作流程

sed工作流程

读取

sed从输入流(文件,管道,或标准输入)读取,并将其存储在其内部的缓冲模式称为缓冲行。

执行

所有sed命令顺序地对模式缓冲区使用。默认情况下,sed命令都适用于所有行(全局),除非指定行寻址。

显示

sed发送(修改)的内容到输出数据流。在发送数据后,模式缓冲器是空的。这个过程一直重复,直到文件被耗尽。

安装

linux

现在的linux系统一般都自带有sed命令; 直接使用即可;

windows

这个链接中可以下载

下载得到的是一个压缩包, 解压后里面的bin目录中有sed(还有awk和grep).

【丫的在windows下解析中文字符会遇到编码问题....还不如python简单点.】

使用格式

sed OPTIONS . . . [SCRIPT] [INPUTFILE . . .] 

常用的选项(OPTIONS)

-n

说明: 禁用自动打印功能

举例:

[root@iZwz99yuz3z7ch8g38k41jZ ~]# seq 3 | sed ' '
1
2
3
[root@iZwz99yuz3z7ch8g38k41jZ ~]# seq 3 | sed -n ' '
[root@iZwz99yuz3z7ch8g38k41jZ ~]#

-e

说明:处理输入时, 将脚本中的命令都添加到准备运行的命令集中

举例

[root@iZwz99yuz3z7ch8g38k41jZ ~]# seq 5|sed -ne '3d' -e '1d' -e '2,5p'
2
4
5
[root@iZwz99yuz3z7ch8g38k41jZ ~]#

-f

说明处理输入时, 从脚本文件中读取命令,并将其添加到准备运行的命令集中; -f能和-e组合一块用;

举例

[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat command.txt
3d
1d
2,5p
[root@iZwz99yuz3z7ch8g38k41jZ sed]# seq 5|sed -nf command.txt
2
4
5
[root@iZwz99yuz3z7ch8g38k41jZ sed]#

-s

默认情况下,sed会把命令行上指定的多个文件视为一个单独的连续的长流(也就是合并所有文件成一个大文件)。使用-s选项后, 会将这些文件视为各自单独的文件.

示例见下面-i选项中的示例;

-i[SUFFIX]

说明: 一般sed只会将处理后的字符串发送到终端屏幕上; 使用-i选项后, 后不接字符串时, 会将修改后的结果直接保存到原始文件中;

如果后带SUFFIX, 则会另存原始文件, 新文件名为原文件名[SUFFIX]; 同时, 修改后的字符串会保存到原始文件中;

由于-i后可指定后缀, 所以-i后不能直接接其他操作. 如-if, 会将f视为后缀; 应该修改为-fi;

该选项隐含指定选项 -s

示例可见下面的-i操作中的示例;

[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt
1) A Storm of Swords, George R. R. Martin, 1216
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed 's/1216/2023/' books.txt
1) A Storm of Swords, George R. R. Martin, 2023
# 不影响原文件内容
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt
1) A Storm of Swords, George R. R. Martin, 1216
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed -i 's/1216/2023/' books.txt
# 由于使用了-i, 且没接后缀, 所以直接将结果保存到原文件中
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt
1) A Storm of Swords, George R. R. Martin, 2023
# 这里由于在-i后指定了后缀_old, 所以命令执行后, 会将原来的books.txt另存为books.txt_old; 然后修改后的字符串会存在books.txt中
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed -i_old 's/2023/1216/' books.txt
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt
1) A Storm of Swords, George R. R. Martin, 1216
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt_old
1) A Storm of Swords, George R. R. Martin, 2023
[root@iZwz99yuz3z7ch8g38k41jZ sed]#
# 下面演示了-i 命令暗含 -s 命令
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt books.txt_old
1) A Storm of Swords, George R. R. Martin, 1216
1) A Storm of Swords, George R. R. Martin, 2023
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed  "1s/Storm/wind/" books.txt books.txt_old
1) A wind of Swords, George R. R. Martin, 1216
1) A Storm of Swords, George R. R. Martin, 2023
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed -s "1s/Storm/wind/" books.txt books.txt_old
1) A wind of Swords, George R. R. Martin, 1216
1) A wind of Swords, George R. R. Martin, 2023
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed -i "1s/Storm/wind/" books.txt books.txt_old
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat books.txt books.txt_old
1) A wind of Swords, George R. R. Martin, 1216
1) A wind of Swords, George R. R. Martin, 2023
[root@iZwz99yuz3z7ch8g38k41jZ sed]#

-r/-E

使用扩展正则表达式,而不是基础正则表达式。

sed命令在不适用-r/-E参数时, 能使用基础正则表达式

基础正则表达式和扩展正则表达式的区别

基础正则表达式(BRE)和扩展正则表达式(ERE)是模式中的两种语法变体。sed默认的正则表达式是BRE语法,与grep类似。使用POSIX指定的“-E”选项(或者“-r”、“–regexp-extended”)启用ERE语法。
  在GNU sed中,基础正则表达式和扩展正则表达式之间的唯一区别是一小部分特殊字符的行为不同:?、+、(、)、{、}和 | 。
  对于BRE语法,这些字符没有特殊意义,除非加前缀的反斜杠(“\”)才具有特殊意义;而对于ERE语法,这些字符刚好相反的:这些字符是特殊的,除非加前缀的反斜杠(“\”),才变成了字面意义上的字符。

期望模式BRE语法ERE语法
字面上的 + (加号)$ echo ‘a+b=c’ > foo
$ sed -n ‘/a+b/p’ foo
a+b=c
$ echo ‘a+b=c’ > foo
$ sed -E -n ‘/a+b/p’ foo
a+b=c
一个或多个a字符后跟b(加号作为特殊元字符)$ echo aab > foo
$ sed -n ‘/a+b/p’ foo
aab
$ echo aab > foo
$ sed -E -n ‘/a+b/p’ foo
aab
BRE和ERE区别举例

退出状态码

0代表成功; 非0代表失败;

可以通过q/Q命令来自定义退出状态码; 可能在shell中可能用来做判断;

示例

[root@iZwz99yuz3z7ch8g38k41jZ sed]# seq 2 | sed ' '
1
2
[root@iZwz99yuz3z7ch8g38k41jZ sed]# echo $?
0
[root@iZwz99yuz3z7ch8g38k41jZ sed]# cat no
cat: no: No such file or directory
[root@iZwz99yuz3z7ch8g38k41jZ sed]# sed ' ' no
sed: can't read no: No such file or directory
[root@iZwz99yuz3z7ch8g38k41jZ sed]# echo $?
2
[root@iZwz99yuz3z7ch8g38k41jZ sed]# seq 2 | sed 'q10'
1
[root@iZwz99yuz3z7ch8g38k41jZ sed]# echo $?
10
[root@iZwz99yuz3z7ch8g38k41jZ sed]#

常见的命令

使用格式中的脚本([SCRIPT])是由一个个的命令组成的; 这里说明其中几个常见的命令;

命令格式

[addr]X[options]

X是一个单字符的sed命令占位符。[addr]是一个可选的行地址。如果指定了[addr],命令X只只会在匹配的行上执行。[addr]可以是一个单独的行号、一个正则表达式或者一系列连续的行范围——地址范围。[options]是用在某些sed命令的选项。

举例

[root@iZwz99yuz3z7ch8g38k41jZ sed]# seq 19 | sed '10,13s/1/2/'
1
2
3
4
5
6
7
8
9
20
21
22
23
14
15
16
17
18
19
[root@iZwz99yuz3z7ch8g38k41jZ sed]#

上面的例子将第10到13行的数字1用数字2进行替换;

其中:

  • 10,13: 就是[addr], 指第10到13行;
  • s: 就是X, 是替换命令(substitute);
  • /1/2/: 就是[options], 这里是将每行的第一个1用2替换;

脚本中或脚本文件中的命令可以用分号(;)或换行(ASCII 10)分隔;(一般都是分号分隔)。也可以使用‘-e’或‘-f’选项指定多个脚本。

具体命令符说明

s 替换命令

替换命令s(substitute的缩写)可能是sed中最重要的命令,它有许多选项。s命令的语法是:

's/regexp/replacement/flags'

它的基本概念是简单的:s命令试图用搜索正则表达式(regexp)去匹配模式空间;如果匹配成功,那么就用替换部分(replacement)替换模式空间中匹配的部分。

替换部分可以包含表达式‘\n’反向引用(n是一个从1到9的数字,包含1和9),‘\n’表示引用正则表达式匹配的第n个左右圆括号“(, )”包围的部分。此外,替换部分可以包含未转义的‘&’字符,它引用模式空间中的匹配部分的全部。

在任何给定的s替换命令中,三个‘/’字符可以统一变更为任何其他单个字符(译者:例如,s/foo/bar/ --> s$foo$bar$)。如果在正则表达式或者替换内容中出现‘/’字符(或者其他替代字符),那么,在这个字符前面必须添加一个‘\’字符进行转义。

举例:

root@centos8: sed$ echo "abc123.abcf5678xyz987!"|sed -E 's~([a-z]*)([0-9]*)([\.!]?)~^\3\2\1$~g'
^.123abc$^5678abcf$^!987xyz$
root@centos8: sed$

上面没有使用常见的'/'做分隔符; 而是使用了'~'作为分隔符;

-E说明使用了扩展正则表达式. 避免大量使用'\'进行转义. 指明使用扩展正则的话, 就需要这样写

root@centos8: sed$ echo "abc123.abcf5678xyz987!"|sed 's~\([a-z]*\)\([0-9]*\)\([.!]\?\)~^\3\2\1$~g'
^.123abc$^5678abcf$^!987xyz$

上面这个命令中的\3 \2 \1 分别对应着正则表达式中的第3个括号:[.!]?, 第二个括号:[0-9]*, 第一个括号:[a-z]*中匹配到的内容. 匹配到后, 将次序调换;

可以看到,上面这个表达式, 在给出的字符串中,共匹配到了3次, 所以分别对这三次匹配的内容的开头加^符号, 在结尾加$ 符号, 并且按要求交换顺序; 就得到了最终的结果.

作为GNU sed扩展,可以包括有一个‘\’和一个字符组成的序列,这个字符只能是 L、l、U、u或E中的一个。具体含义如下:

\L (lowercase小写)在遇到‘\U’或者‘\E’以前,把替换内容(replacement)全部转化成小写。

\l  (lowercase小写)把下一个字符转换成小写。

\U  (uppercase大写)在遇到‘\L’或者‘\E’以前,把替换内容全部转化成大写。

\u  (uppercase大写)把下一个字符换成大写。

\E  (end停止)终止由‘\L’或者‘\U’开始的大小写转换。

  当使用g(global全局)标志(flag)时,大小写转换会从正则表达式的一个匹配传播到另一个。例如,在模式空间中包含字符串“a-b-”下,执行以下命令时:

echo 'a-b-' | sed  's/\(b\?\)-/x\u\1/g'

  输出是“axxB”。当替换第一个匹配项‘-’为‘x’时,‘\u’序列只影响由‘\1’反向引用的空字符,在替换第二个匹配项‘b-’为‘xB’时,不会影响前面已经加到模式空间的‘x’。

如果没有g标志,只会替换出现的第一个匹配项:

echo 'a-b-'| sed  's/\(b\?\)-/x\u\1/'

  输出是axb-。

s命令可以跟随下列0个或0个以上的标志:

g          (global全局)替换应用于匹配regexp的所有项,而不仅仅是第一匹配项。

number      只替换第number个匹配regexp项。
            注意:在与s命令交互时,当您同时使用g和number标志时,POSIX标准没有指定应该发生什么,并且当前在各个sed版本
            实现中没有取得一直意见。对于GNU sed而言,对这种情况的交互是:忽略第number个前面的匹配项,匹配和替换
            第number个及其后面的匹配项。

p           (print打印)如果替换成功,则打印替换成功后的模式空间内容。
            注意:当同时指定了p和e选项,这两个选项的相对次序不同产生非常不同的结果。一般而言,“ep”(译者:先执行模式空间
            找到的命令后打印)是您想要的,但是,反过来操作对于调试很有用。正因如此,当前GNU sed版本对在e之前和之后是否
            存在p标志作特别解释,即在执行之前打印和执行之后打印模式空间。而在一般情况下,s命令的标志会只显示它们的
            效果一次。尽管有文档记录,但这种行为在未来的版本中可能会改变。

w filename
            (write写)如果s命令替换成功,那么把替换后的结果写出到以filename为文件名的文件中。作为GNU sed扩展,提供了
            两种特殊的文件名:“/dev/stderr”,把结果打印到标准错误;“/dev/stdout”,把结果打印到标准输出。(如果
            没有使用‘-i’,与p命令等价)

e           (execute执行)此命令允许将shell命令通过管道输入并传输到模式空间,如果s命令替换成功,e执行在模式空间中找到
            的命令,并用执行命令输出结果替换模式空间,尾随的换行符被抑制;如果要执行的命令包含nul字符,则结果未定义。
            该标志是GNU sed扩展。

I
i           (insensitive不敏感)用I修饰正则表达式的匹配是GNU sed的扩展,它使sed以不区分大小写的方式匹配正则表达式
            (regexp)。

M
m           (multi-line多行)用M修饰正则表达式的匹配是GNU sed的扩展。它指示GNU sed在多行模式下匹配正则表达式
            (regexp)。该修饰符           引起‘^’和‘$’分别匹配换行后空字符和空字符后换行(除了正常行为之外)。特殊字符
            序列(\‘ 和 \’)一直分别匹配缓冲区的开始和结尾。

简单举例

root@centos8: sed$ echo "123456" | sed 's/[0-9]/A/'       # 将第一次出现的数字替换为A
A23456
root@centos8: sed$ echo "123456" | sed 's/[0-9]/A/g'       # 将每个数字替换为A
AAAAAA
root@centos8: sed$ echo "123456" | sed 's/[0-9]/A/2'       # 将第二次出现的数字替换为A
1A3456
root@centos8: sed$ echo "123456" | sed 's/[0-9]/A/2g'       # 将第二次及之后出现的数字替换为A
1AAAAA
root@centos8: sed$

至于将第2次和第3次出现的数字替换为A, 或许简单使用sed获取无法实现.

  另外,在多行模式下,‘.’句点字符不会匹配换行字符。

常用命令

  除替换命令外的一些常见命令。

#  [不允许有地址]
            字符“#”开始一个注释,一直到换行为止。
            如果您关心可移植性,注意一些不遵循POSIX标准的sed实现可能只支持单行注释,且脚本的注释行第一个字符只能是“#”才行。
            警告:如果sed脚本的前两个字符是#n,则强制使用“-n”(禁用自动打印)选项。如果要在脚本的第一行中放置注释,并且该
            注释以字母“n”开头,并且不希望出现上述的行为,请您确保使用大写字母“N”,或者在“n”前面至少放置一个空格。

q[exit-code]
            没有处理更多命令或者输入,退出sed。例如:在打印输入的第二行后停止:
            $ seq 3 | sed 2q
            1
            2
            该命令只接受一个地址。注意如果没有使用“-n”禁用自动打印,会打印当前模式空间。从sed脚本返回退出值的能力是
            GNU sed的扩展。另请参见GNU sed扩展Q命令,该命令在不打印当前模式空间的情况下安静地退出。

d           删除模式空间,立即启动下一轮循环。例如:删除第二输入行:
            $ seq 3 | sed 2d
            1
            3

p           打印模式空间内容到标准输出。此命令通常只与“-n”命令行选项一起使用。例如:只打印输入的第二行:
            $ seq 3 | sed -n 2p
            2

n           如果自动打印没有被禁用,打印模式空间,然后,无论如何,用下一输入行内容替换模式空间。如果没有更多的输入,
            则不执行更多命令退出sed。
            此命令用于跳过一输入行(例如,处理每个N行)。例如:完成每个3行的替换(2个n命令跳过2行):
            $ seq 6 | sed ‘n; n; s/./x/’
            1
            2
            x
            4
            5
            x
            GNU sed提供一个扩展的地址语法:“开始~步长”达到相同的结果:
            $ seq 6 | sed ‘0~3s/./x/’

{ cmds}     可以把一组命令放在‘{’和‘}’字符之间,形成命令组。当您希望一组命令由一个地址或地址范围匹配触发时,这特别有用。
            例如:完成替换然后打印第二输入行:
            $ seq 3 | sed -n ‘2{s/2/X/; p}’
            X
            
d 删除命令
root@centos8: sed$ seq 3| sed '2d'
1
3
root@centos8: sed$
e 执行找到的命令
root@centos8: sed$ echo "uname" | sed 'e'
Linux
root@centos8: sed$echo "uname -a" | sed 'e'
Linux centos8 4.18.0-193.28.1.el8_2.x86_64 #1 SMP Thu Oct 22 00:20:22 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
root@centos8: sed$
# e命令也可以用s命令替换
root@centos8: sed$ echo "ls
> pwd"|sed -E "s/(.*)/\1/e"
books.txt
books.txt_old
book.txt
commands.txt
command.txt
new_command.txt
quote.txt
trade_order.txt
/root/linux_learn/sed
root@centos8: sed$
F 打印输入文件的文件名
root@centos8: sed$ sed 'F' command.txt
command.txt
3d
command.txt
1d
command.txt
2,5p
root@centos8: sed$ sed -n 'F' command.txt
command.txt
command.txt
command.txt
root@centos8: sed$
q[exit-code] 打印模式空间内容并退出
Q[exit-code] 不打印模式空间内容并退出
root@centos8: sed$ seq 3 | sed 'q2'
1
root@centos8: sed$ echo $?
2
root@centos8: sed$ seq 3 | sed 'Q101'
root@centos8: sed$ echo $?
101
root@centos8: sed$
p 打印模式空间内容
root@centos8: sed$ seq 5 | sed -n '1,3p'
1
2
3
root@centos8: sed$
y 字符转换

y/source-chars/dest-chars/
如果模式空间中的任何字符匹配源字符集(source-chars)的字符,那么逐个转换成目标字符集(dest-chars)的相对应位置 的字符。

root@centos8: sed$ echo 'fedcba650' | sed 'y/abcdef/123456/'
654321650
root@centos8: sed$
l 可视化(指特殊字符)打印模式空间内容

以可视的形式打印模式空间的内容。(译者:这形式是指将不可打印的字符打印成可显示 的C语言式样的 转义字符,例如相应的转义序列打印成字符 \a、\b、\f、\r、\t和\v等,用“\”表示换行,并在打印行的结尾 添加一个“$”。)

root@centos8: sed$ seq 3 | sed 'l'
1$
1
2$
2
3$
3
root@centos8: sed$
w 把模式空间的内容写到以‘filename’为文件名的文件中
root@centos8: sed$ cat command.txt
3d
1d
2,5p
root@centos8: sed$ sed 'w new_command.txt' command.txt
3d
1d
2,5p
root@centos8: sed$ cat new_command.txt
3d
1d
2,5p
root@centos8: sed$

另外, 还有a, c, i这些进行替换, 文本插入的命令,但基本上都能用s命令代替

  • a text 在行后添加文本“text”。
  • c text 用文本“text”替换一行或多行。
  • i text 在当前行前插入文本“text”。
# s对a的等价替换
# seq 3 | sed 'a abc'   <==>   seq 3 | sed -E 's/(.*)/\1 \nabc/'
root@centos8: sed$ seq 3 | sed 'a abc'
1
abc
2
abc
3
abc
root@centos8: sed$ seq 3 | sed -E 's/(.*)/\1\nabc/'
1
abc
2
abc
3
abc
root@centos8: sed$
# s对c的等价替换
# seq 3 | sed 'c aaa' <==> seq 3 | sed 's/.*/aaa/'
root@centos8: sed$ seq 3 | sed 'c aaa'
aaa
aaa
aaa
root@centos8: sed$ seq 3 | sed 's/.*/aaa/'
aaa
aaa
aaa
root@centos8: sed$
# s对c的等价替换
# seq 3 | sed 'i 123' <==> seq 3 | sed -E 's/(.*)/123\n\1/'
root@centos8: sed$ seq 3 | sed 'i 123'
123
1
123
2
123
3
root@centos8: sed$ seq 3 | sed -E 's/(.*)/123\n\1/'
123
1
123
2
123
3
root@centos8: sed$

地址:选择行

sed中,可以指定命令在哪些行起作用.即命令格式中的addr.

简单举例

root@centos8: sed$ seq 5 | sed 'd'
root@centos8: sed$ seq 5 | sed '1d'
2
3
4
5
root@centos8: sed$ seq 5 | sed '2,3d'
1
4
5
root@centos8: sed$ 

通过数字选择行

  sed脚本中的地址可以采用以下任何形式:

number         指定行号,只会匹配输入的那一行。(注意,除非指定了“-i”或“-s”  选项,否则sed会对所有输入文件中的行进行
                连续计数。)

$               此地址与输入的最后一个文件的最后一行匹配,或者在指定“-i”或“-s”选项时与每个文件的最后一行匹配。

first~step      (开始~步长)这是GNU扩展。匹配从第first行开始的每个步长行。尤其是,当存在非负n、且当前行的行号等于
                “first +(n * step)”时将被选择。因此,用1~2来选择奇数行,用0~2来选择偶数行;用2~3来选择从第二行开始的
                每三行;用10~5来选择从第十行开始的每5行;用50~0来表示50只是一种模糊的方式。以下命令演示步长地址的用法:
                $ seq 10 | sed -n ’0~4p’
                4
                8
                $ seq 10 | sed -n ’1~3p’
                1
                4
                7
                10
                

通过文本匹配选择行

  GNU sed支持下列正则表达式地址。。如果使用了“–E” 或“–r”选项,就会使用扩展正则表达式格式进行匹配。

/regexp/           这将选择所有匹配正则表达式regexp的行。如果regexp本身包含任何“/”字符,其中的每一个“/”必须使用
                    反斜杠(\)转义。

                    下面的命令打印在“/etc/passwd”文件中以“bash”结尾的所有行:
                    (当然,还有很多其他方法可以做到这一点,例如:
                    grep ’bash$’ /etc/passwd
                    awk -F: ’$7 == "/bin/bash"’ /etc/passwd
                    )
                    sed -n ‘/bash$/p’ /etc/passwd
                    空正则表达式“//”重复上一个匹配成功的正则表达式(如果将空正则表达式传递给s命令,也具有相同的功能)。

                    请注意,正则表达式的修饰符(译者:标志)是在编译正则表达式时评估的,因此与空正则表达式一起指定
                    修饰符是无效的。
\%regexp%
                    (这个“%”符号可以由其他任意单个字符替代。)
                    这也会匹配正则表达式regexp,但允许使用与“/”不同的分隔符。如果regexp本身包含大量斜杠(/)时,
                    这尤其有用,因为它避免了对每个“/”进行冗长转义。如果regexp本身包含任何分隔符字符,则每个字符
                    都必须用反斜杠(\)转义。

                    下面的三个命令都是等价的,其中两个更换了分隔符。它们打印以 /home/alice/documents/ 开头的所有行:
                    sed -n ’/^\/home\/alice\/documents\//p’
                    sed -n ’\%^/home/alice/documents/%p’
                    sed -n ’\;^/home/alice/documents/;p’
/regexp/I
\%regexp%I
                    正则表达式的I修饰符是GNU扩展,它使regexp以不区分大小写的方式进行匹配。

                    在许多其他编程语言中,小写字符i用于不区分大小写的正则表达式匹配。但是,在sed中,i用于insert命令
                    (参见[insert command插入命令])。

                    观察下面例子的不同之处。

                    在这个示例中,“/b/I”是一个地址,使用I修饰符的正则表达式,d是删除命令:
                    $ printf "%s\n" a B c | sed ’/b/Id’
                    a
                    c
                    这里,“/b/”是一个正则表达式地址,i是插入命令。d是要插入的值。一行内容为d被插入到匹配行的上面:
                    $ printf "%s\n" a b c | sed ’/b/id’
                    a
                    d
                    b
                    c
/regexp/M
\%regexp/M
                    正则表达式的修饰符M是GNU sed的扩展。它指示GNU sed除了正常的行为之外,在多行模式下去匹配正则表达式。
                    该修饰符引起“^”和“$”分别匹配换行之后的空字符和换行之前的空字符。有一些特殊的字符序列(\‘和 \’)总是与
                    缓冲区的开始或结束相匹配。此外,“.”句点字符在多行模式下不匹配换行符。

                    正则表达式地址决定是否处理当前模式空间的内容。如果模式空间内容发生了改变(例如使用“s///”替换命令),
                    正则表达式会匹配变化后的文本。

                    下面的例子中,自动打印被“-n”禁止。“s/2/X/”替换命令改变了行包含的 2 到 X。/[0-9]/p命令匹配包含数字的行,
                    然后打印它们。由于第二行在“/[0-9]/”正则表达式起作用之前已经改变,所以,就不能匹配到,也不会打印出来:
                    $ seq 3 | sed -n 's/2/X/ ; /[0-9]/p'
                    1
                    3

范围地址

  可以通过由逗号(,)分隔的两个地址来指定地址范围。地址范围匹配从第一个地址匹配的位置开始的行,并继续直到第二个地址匹配为止(含前后两个地址):

$ seq 10 | sed -n ’4,6p’
4
5
6

  如果第二个地址是regexp,那么检查结束匹配将从与第一个地址匹配行后面的行开始搜索:范围将始终跨越至少两行(当然输入流结束的情况除外)。

$ seq 10 | sed -n ’4,/[0-9]/p’
4
5

  如果第二个地址是小于或等于第一个地址匹配行的数字,则只匹配的那一行:

$ seq 10 | sed -n ’4,1p’
4
12

  GNU sed也支持一些特殊的两地址形式;所有这些都是GNU扩展:

0,/regexp/         在地址规范中行号为0可以使用,如“0,/regexp/”,sed会试图在第一输入行上进行匹配。话句话说,
                    “0,/regexp/”与“1,/regexp/”功能类似,区别是地址“/regexp/”是否与第一输入行匹配。因为“0,/regexp/”
                    格式的“/regexp/”会从第一输入行开始搜索,如果匹配成功,则会结束地址范围。而“1,/regexp/”格式
                    的“/regexp/”会试图从第二输入行开始搜索,从而使地址范围扩展到该正则表达式匹配到的行为止。

                    请注意,这是0地址唯一有意义的地方;输入流中没有第0输入行,以任何其他方式给0地址的命令都会出错。

                    以下示例演示了从地址1开始与从地址0开始的区别:
                    $ seq 10 | sed -n ’1,/[0-9]/p’
                    1
                    2
                    $ seq 10 | sed -n ’0,/[0-9]/p’
                    1
addr1,+N            匹配第addr1行和addr1后面的N行。
                    $ seq 10 | sed -n ’6,+2p’
                    6
                    7
                    8
addr1,~N            匹配第addr1行及其后面的行,直到输入行号是n的倍数。以下
                    命令从第6行开始打印,直到下一行是4的倍数(即第8行):
                    $ seq 10 | sed -n ’6,~4p’
                    6
                    7
                    8
                    $ seq 100 | sed -n '8,~13p'
                    8
                    9
                    10
                    11
                    12
                    13
                    addr1可以是一个行号或者一个正则表达式。

! 地址取反

上面介绍的几种方法都是用来指定命令生效的行; 可以通过地址取反操作来指定在哪些行上不执行后续命令. 方法是在命令字母之前,地址声明之后添加一个“!”字符对匹配状态取反。

举例

root@centos8: sed$ seq 6 | sed '1,3d'
4
5
6
root@centos8: sed$ seq 6 | sed '1,3!d'   # 在1到3行之外的行上执行d命令
1
2
3
root@centos8: sed$

同样的, 对于通过文本匹配的也能使用"!"进行取反操作

root@centos8: sed$ cat fruits.txt
apple
banana
origin
watermelon
root@centos8: sed$ sed -E '/banana/s/(.*)/\U\1/' fruits.txt
apple
BANANA
origin
watermelon
root@centos8: sed$ sed -E '/banana/! s/(.*)/\U\1/' fruits.txt
APPLE
banana
ORIGIN
WATERMELON
root@centos8: sed$

参考

sed工作流程

Windows-awk、grep、sed使用及配置

GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(一)

最后修改日期: 2023年11月7日

作者

留言

撰写回覆或留言