sed 流编辑器

个人觉得 sed 的主要优势就是可以实现文本处理自动化。流编辑器会在编辑器处理数据之
前提供一组规则,这点与交互式编辑器不同。

命令格式

1
2
3
4
5
6
7
8
9
10
sed [OPTION]... {script-only-if-no-other-script} [input-file]...

-n, --quiet, --silent
suppress automatic printing of pattern space

-e script, --expression=script
add the script to the commands to be executed

-f script-file, --file=script-file
add the contents of script-file to the commands to be executed

script参数指定了应用于数据流上的单个命令,可以使用-e参数来指定多个命令,或者
使用-f参数指定 sed 脚本文件。

替换命令 s

1
[range]s/regexp/replacement/flags

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ echo "I do not like qt" | sed 's/do not/do/'
I do like qt

$ cat data4
Creating Qt Projects in Creator
Developing Qt Quick Applications with Creator
Building and Running Applications in Creator

$ sed 's/Applications/App/' data4
Creating Qt Projects in Creator
Developing Qt Quick App with Creator
Building and Running App in Creator

$ sed -e 's/Applications/Apps/; s/Creator/mind/' data4
Creating Qt Projects in mind
Developing Qt Quick Apps with mind
Building and Running Apps in mind

$ sed -e '
quote> s/Applications/Apps/
quote> s/Creator/mind/' data4
Creating Qt Projects in mind
Developing Qt Quick Apps with mind
Building and Running Apps in mind

第四和第五个命令是一样的,只是写法不同。注意多个命令时,最后的单引号要和文件在同
一行上,shell 在接收到最后的单引号时就会开始执行命令,这时会从标准输入读取,而不
会读取文件。这里替换命令跟 vim 中的几乎一样(我不清楚他们的正则表达式语法是否相
同),如果你经常使用 vim 下的这个命令,会感到非常亲切。

也可以将命令写到文件里,如下所示:

1
2
3
4
5
6
7
$ sed cat script.sed
s/Applications/Apps/
s/Creator/mind/
$ sed -f script.sed data4
Creating Qt Projects in mind
Developing Qt Quick Apps with mind
Building and Running Apps in mind

这种情况下分号不是必须的。

替换命令的 flag 包括:

cmd description
n 替换第 n 个出现的匹配
g 替换所有的匹配
p 输出修改过的行
w file 将结果写到 file 中

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$ cat data1
text
text text
text text text
text text text text
text text text text text

$ sed 's/text/changed/3' data1
text
text text
text text changed
text text changed text
text text changed text text

$ sed -n 's/text/changed/3p' data1
text text changed
text text changed text
text text changed text text

$ sed -n 's/text/changed/4p' data1
text text text changed
text text text changed text

$ sed -n 's/text/changed/4pw output1' data1
text text text changed
text text text changed text

$ cat output1
text text text changed
text text text changed text

$ sed 's/text/changed/g' data1
changed
changed changed
changed changed changed
changed changed changed changed
changed changed changed changed changed

$ sed 's/text/changed/3g' data1
text
text text
text text changed
text text changed changed
text text changed changed changed

可以看到 flag 也是可以组合的。一般标志p-n一起使用,-n使得 sed 默认不输出。
最后一个例子比较有意思,可以看到 sed 从第 3 个匹配开始替换了其后所有匹配到的文本。

sed 的分隔符并不一定要是/,比如:

1
2
$ which env | sed 's+/usr/bin+/bin+'
/bin/env

这里使用了+,可以根据需要尝试。

sed 的所有命令都可以指定作用范围,范围的表示可以有多种,比如用行号或正则表达式,
请看以下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ cat data5
one text
two text
three three text
four four four text
five five five five text

$ sed '2s/text/changed/' data5
one text
two changed
three three text
four four four text
five five five five text

$ sed '2,4s/text/changed/' data5
one text
two changed
three three changed
four four four changed
five five five five text

$ sed '/^two/s/text/changed/' data5
one text
two changed
three three text
four four four text
five five five five text

$ sed '/^two/,/^four/s/text/changed/' data5
one text
two changed
three three changed
four four four changed
five five five five text

范围可以指定某些行,也可以指定两行之间。详细说一下最后一个例子。

/^two/,/^four/s/text/changed/中的/^two/,/^four/表示一个范围,由两个正则表达
式来表示,正则表达式是/之间的文本,/^two/表示以 two 开头的行,/^four/表示
以 four 开头的行,后面是一个替换命令s/text/changed/将 text 替换为 changed。注
意包围正则表达式的/不能随意更换,这与替换命令中的/不同。

再看下面一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat data7
one text up left
two text up left
three three text up left
four four four text up left
five five five five text up left

$ sed -e '1,2{s/text/changed/; s/left/right/}; 3,4s/up/down/' data7
one changed up right
two changed up right
three three text down left
four four four text down left
five five five five text up left

命令1,2{s/text/changed/; s/left/right/}; 3,4s/up/down/的作用是,在 1~2 行和
3~4 行上分别执行不同的命令,其中 1~2 行上针对每行执行两条命令,分别是两个替换。

删除命令 d

这个命令就非常简单了,看下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cat data
one
two
three
four
five
six
$ sed '2,4d' data
one
five
six
$ sed -n '/^f/{p;d}' data
four
five

最后一个命令使得被匹配到的行在删除前打印出来。

插入命令i 和 附加命令a

这两个命令分别在指定行的前或后增加新行,格式为:

1
sed '[range]cmd\<text>'

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ cat data
one
two
three

$ sed '2i\------' data
one
------
two
three

$ sed '2a\------' data
one
two
------
three

$ sed '2{
quote> i\------
quote> a\------
quote> }' data
one
------
two
------
three

$ sed '2i\
quote> --------\
quote> ========' data
one
--------
========
two
three

注意在i/a之后\后面的内容(除了单引号)会被认为是要插入文本的一部分,所以结尾
的花括号不能与命令写在同一行上。要插入或附加多行文本的话,需要在除最后一行之外每
行的末尾加上\

行修改命令 c

格式与插入附加命令一样,只是c会将原本的内容删除,替换为新的文本。如下:

1
2
3
4
5
6
$ sed '2c\------\
quote> ======' data
one
------
======
three

字符转换命令 y

格式为:

1
[range]y/oldchars/newchars/

oldcharsnewchars的长度必须相同,命令会把指定行中的oldchars对应地转换为
newchars中的字符,无法限定转换特定位置的字符。如:

1
2
3
4
$ sed 'y/to/-=/' data
=ne
-w=
-hree

其他打印命令 p = l

他们的意义分别为:

cmd description
p 打印文本行
= 打印行号
l 打印不可见字符

p命令与替换命令中的标识 p 相似,表示输出该行,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sed -n '2p' data
two

$ sed '=' data
1
one
2
two
3
three

$ cat data2
SPACE TAB NEWLINE
$ sed -n 'l' data2
SPACE TAB\tNEWLINE$

读取命令 r 和 写入命令 w

直接看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat data
one
two
three

$ sed '2r data2' data
one
two
SPACE TAB NEWLINE
three

$ sed -n '2,$w output' data

$ cat output
two
three

其中,2,$w output表示将从第 2 行开始到文件末尾输出到文件output

0%