sed

Table of Contents

sed是非交互式的流编辑器(stream editor),用来对文本增改删等操作。默认情况下sed不会修改文本源本身的内容。

1 基本命令

sed最常用的两个命令是“替换”和“删除”。

1.1 替换

第一个语法,最简单也是最常用的:

sed 's/old/new/' file
  • s:sed的操作命令;
  • /../../ :限定符,限定符也可以用其他符号,比如叹号:sed 's!root!ROOT!' /etc/passwd
  • old:查找字符串
  • new:替换的新字符串

1.1.1 实例

1、替换换行符:

sed 'N;s/\n/ /g'

2、替换多个空格为一个空格:

sed "s/\s\+/ /"

如果old和new有”/“,就需要做转义:\/

3、给文本文件每行开头结尾加字符开头和结尾:

sed 's/^/xxx/'
sed 's/$/xxx/'

4、使用正则,将IP替换成星号:

sed -r 's/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/***/' /etc/hosts

&:把内容替换成正则匹配到的内容。例,以下的匹配会给数字前后加上“@”:

$ echo 'abc123' | sed -r 's/[0-9]+/@&@/'
abc@123@

5、替换16进制内容,如替换Hive的输出分隔符(\x01):

sed 's/\x01/ /g'

注意:只有GNU sed才支持16进制表示,如果是BSD系统,可以用下面这种方式:

sed  's/'$(echo "\001")'/ /g'

1.1.2 flag

s命令更详细的用法:

s/pattern/replacement/flags

flags:

  • n,表示对指定模式第n次匹配进行替换
  • g,全局替换
  • p,打印模式的内容
  • I:忽略大小写

1.2 删除

删除命令也是非常常用的,用来删除匹配的行。删除命令是:d。

例,删除匹配到“root”的行:

sed '/root/d' /etc/passwd

可以为删除指定地址范围,如,删除1~10行:

sed '1,10d'

表示从20行开始删到行尾:

sed '20,$d'

表示从第一行开始一直删除到第一次匹配到字符“c”的行:

sed '1,/c/d'

表示删除空行:

sed '/^$/d'

1.3 追加、插入、更改、替换

1.3.1 追加

语法:[位置]a\[内容]

$ echo abc | sed '1a\test'
abc
test

1.3.2 插入

插入是在指定位置前面插入,和追加不一样,语法:[位置]i\[内容]。

$ echo abc | sed '1i\test'
test
abc

1.3.3 更改

语法:[位置]c\[内容]。

$ echo -e 'aaa\nbbbb\nccccc' | sed '1c\test'
test
bbbb
ccccc

1.3.4 替换

语法:[位置]y/abc/ABC/。

sed 'y/abc/ABC/'

表示每个出现的“a”都被替换成“A”、“b”被替换成“B”、“c”被替换成“C”。

1.4 多命令

如何让sed一次执行多个操作。

方法1,使用-e参数:

sed  -e 's/,/\t/g' -e 's/(//g' -e 's/)//g'

方法2,指定sed脚本:

s/root/ROOT/
s/\/bash/\/zsh/ # 替换shell

注:“#”是sed的注释符。

然后通过-f参数指定:

sed -f /tmp/sed.sed /etc/passwd

方法3,使用分号做命令的分割符:

sed 's/,/\t/g;s/(//g;s/)//g'

方法4,使用shell本身的分行功能:

sed 's/,/\t/g
> s/(//g
> s/)//g' test

1.5 next命令

next是一个控制流程的命令,负责读取”下一行“,可以用于完成”删除xx下一行内容“这样的任务。

例,如果匹配”nobody“的下一行是空白行,则删除:

sed '/nobody/{n;/^$/d}' text

1.6 读写文件

w用于写入到文件,如

sed 's/root/ROOT/w new' < /etc/passwd

r则是将其他文件内容读入当前模式空间:

sed -n '/root/r /etc/hosts' /etc/passwd

1.7 打印

/p命令,将操作的行进行打印输出,但是输出内容时并不会清理其他输出,所以可能导致重复的输出,一般来说都搭配-n参数使用:

sed -n 's/root/ROOT/p' /etc/passwd

打印行号用=命令,也可以配合-n使用:

sed -n '/root/=' /etc/passwd

1.8 分组

sed可以在指定范围内做多个操作,多个操作用大括号包围起来,如:

/root/,/gdm/{
s/\/nologin/\/NOLOGIN/g
/adm/d
}

1.9 退出

打印10行后结束运行:

sed '10 q' /etc/passwd

1.10 修改源文件

sed默认是不会修改文本源本身的内容的,如果要把处理结果直接写入源文件,可以用-i 参数:

sed -i 命令

但要尽量避免直接操作,否则sed命令一旦写错,就导致源文本被彻底修改。

2 按范围操作文本

默认情况下,sed的命令是作用于整个文本的,但有时只希望它操作部分行。sed支持指定命令作用于哪行,只需在命令之前指定范围,比如:

sed '1s/o/0/'

表示将文本的第一行的字母“o”替换成数字“0”。

地址也可以用范围表示:

sed '1,3s/o/0/'

表示将1至3行的字母“o”替换成数字“0”。

行尾用”$“表示:

sed '3,$s/o/0/'

表示从第三行开始替换,一直到末行。

甚至可以用正则来表达:

sed '/^c/s/w/W/'

只有匹配到行首是”c“开头的,才做替换工作。

在地址后面加”!“,表示排除。如下,把”a“替换成”A“,除了1~4行:

sed '1,4!s/a/A/'

“!”也可以加在flag部分,比如有一个Web日志文件,内容大致如下:

2017-08-22 17:02:24 222.130.196.* /
2017-08-22 17:02:24 222.130.196.* /images/index.css
2017-08-22 17:02:26 222.130.196.* /video/201701/20170125.jpg
2017-08-22 17:02:24 222.130.196.* /images/logo.png
......

不过日志中某些行是畸形数据,这时可以用sed找出畸形数据:

# 打印非2017开头的行
sed -n '/^2017-/!p'

确认了畸形数据后,加上-i参数就可以重新编辑源文件了:

sed -i -n '/^2017-/p'

跳步是GNU sed独有的功能,可以每跳过几行用sed处理一次,如:

sed '0~3s/a/A/'

从首行开始,每3行执行一次替换操作。

选择范围时,结束范围若是正则表达式,则结束于第一次匹配到的那行,如:

echo -e '1\n2\na\n3' | sed -n '0,/[a-z]/p'

结束范围若以”+“开头,如:3,+4p,表示从第3行开始,往后4行为操作范围:

$ seq 10 | sed -n '3,+2p'
3
4
5

还有一个较少见的:3,~4p,表示结束范围是4的倍数(8),举例:

$ seq 10 | sed -n '3,~2p'
3
4

表示范围是第3行到第4行(2的倍数)。

3 高级命令——多行和流程控制

模式空间(pattern space):sed会从文本中逐行读取,当前行的内容就放在模式空间中的。

3.1 多行替换

通常sed会只读一行到模式空间中,但也可以让它读取多行,假如有这样一段文本:

Published software should be free software. To make it free software, you need to release it under a free software license. We normally use the GNU
General Public License (GNU GPL), but occasionally we use other free software licenses. We use only licenses that are compatible with the GNU GPL for GNU software.

我想将”GNU General Public License (GNU GPL)“替换为”GPL“,但”GNU“后面出现了换行,所以无法直接替换。

sed的”N“命令可以把下一行的内容加入到当前的模式空间中,所以借助”N“命令就可以完成以上需求:

sed '/GNU$/{N;s/GNU\nGeneral Public License (GNU GPL)/GPL/}'

如果匹配到”GNU“结尾的行,就执行”N“命令把下一行内容追加到当前模式空间中后执行替换操作。替换后如下:

Published software should be free software. To make it free software, you need to release it under a free software license. We normally use the GPL, but occasionally we use other free software licenses. We use only licenses that are compatible with the GNU GPL for GNU software.

3.2 多行删除

如下文本:

aaaaaa









aaaaaaaaaaaaaaaaaaa




aaaaa

aaa

现在想每个非空行之间只保留一个空行,这里用到“D”命令:

sed '/^$/{N;/^\n$/D}'

为什么不是”d“?修改成”d“以后,执行结果如下:

$ sed '/^$/{N;/^\n$/d}' test
aaaaaa

aaaaaaaaaaaaaaaaaaa
aaaaa

aaa

用”d“命令时,遇到空行就读入下一行到模式空间,如果新加入模式空间的这行也是空行,那么正则”^$“是可以成功匹配,接着整个模式都被删除了——所以两行都没有了,也就是说如果空行个数是奇数,那可以保留一行空行。

现在修改成”D“命令就正常了:

$ sed '/^$/{N;/^\n$/D}' test
aaaaaa

aaaaaaaaaaaaaaaaaaa

aaaaa

aaa

因为”D“只会删除当前模式空间中第一个匹配到的行,也就是两个空行合并到模式空间中,只有其中一行会被删除。

3.3 多行打印

”p“命令会将当前模式空间内容全部打印出来,而”P“只会打印模式空间中第一行命令:

$ seq 10 | sed -n '/[0-9]/{N;P}'
1
3
5
7
9

当匹配到数字行时,会将下一行也读入当前模式空间,并且只打印模式空间第一行,所以实现了每隔一行打印一次。

3.4 保存空间(hold space)

sed会把每行内容都往模式空间中保存,除了模式空间,还有个保存空间可以使用。

h或H:将模式空间的内容复制到保存空间 G:将保持空间的内容复制到模式空间 x:交换保持空间和模式空间的内容

例,将下面文本中带“1”的行和带“2”的行交换顺序:

1
2
11
22
111
222

命令如下:

$ sed '/1/{h;d};/2/{G}' num_file
2
1
22
11
222
111

命令说明: 1、当匹配到“1”时,先执行“h”,将模式空间内容保存到保持空间,然后删除当前模式空间的内容;

2、当匹配到“2”时,执行“G”从保持空间中将数据复制到模式空间。

4 参考资料