概要
sed 程序是由一个或者多个sed command组成,由一个或多个的-e,-f, --expression, 和–file 选项来传递。如果不使用这些选项,那么第一个非选项参数将会被当做sed脚本
sed命令的语法格式
[addr]X[options]
X表示一个单字符的sed 命令,[addr]表示一个可选的行位置信息,如果指定了[addr],那么命令X只会在匹配的行上执行。[addr]可以是一个单独的行数子、一个正则表达式或是一个行的范围。[options]是被一些sed 命令使用的。如:
- 下面命令将删除输入文件中30到35行的内容,30,35 表示一个行地址范围
sed ’30,35d’ input.txt
- 下面命令会一直打印输入,直到找到以单词foo开头的输入,然后sed会退出,退出状态是42,如果直到文件结束都没有找到匹配的输入,那么sed将会以0作为退出状态。 /^foo/ 是一个正则表达式,q是退出命令,42是命令选项
sed ’/^foo/q42’ input.txt > output.txt
- 脚本或者脚本文件中的多个命令可以使用分号(;)或者换行符来分隔。多个脚本可以使用 -e 或者-f 选项指定,下面示例中的命令都是等价的:
sed ’/^foo/d ; s/hello/world/’ input.txt > output.txt
sed -e ’/^foo/d’ -e ’s/hello/world/’ input.txt > output.txt
echo ’/^foo/d’ > script.sed
echo ’s/hello/world/’ >> script.sed
sed -f script.sed input.txt > output.txt
echo ’s/hello/world/’ > script2.sed
sed -e ’/^foo/d’ -f script2.sed input.txt > output.txt
命令 s 的使用
在sed众多的命令中,s应该是最长使用的命令,没有之一,命令 s 的语法如下:
's/regexp/replacement/flags'
说明:
- 在replacement中可以使用 \n (n是一个从1到9的数字)引用,该引用表示在regexp中由括号 ( 和 ) 括起来的部分,\1 表示由第一组 (、)括起来的部分,\2表示第二组 (、)括起来的部分,以此类推;另外可以在replacement使用符 & 来代替整个regexp匹配的部分;如下示例:
$ printf 'abc123\n' | sed -n 's/\([a-z]*\)[0-9]*/\1/p'
abc
$ printf 'abc123\n' | sed -n 's/\([a-z]*\)\([0-9]*\)/\2/p'
123
$ printf 'abc123\n' | sed -n 's/\([a-z]*\)[0-9]*/&/p'
abc123
- 分隔符“/”,可以由任意其他的单个字符替换,如果分隔符出现在 regexp或者replacement部分中,则需要在其前面使用反斜杠 “\”进行转义 ,如下示例:
$ printf 'abc123\n' | sed -n 's:\([a-z]*\)[0-9]*:&:p'
abc123
$ printf 'abc:123\n' | sed -n 's:\([a-z]*\)\:[0-9]*:&:p'
abc:123
上面示例中,第一条命令使用冒号(:)作为分隔符代替斜杠(/),第二条命令在regexp中出现了分隔符 (:),这时就需要在前面使用反斜杠了。
- 在GNU版的sed的扩展功能中,我们可以使用一些由反斜杠和字母(L, l, U, u, E中的一个)组成的特殊序列表示一下特定的含义,具体如下:
\L 表示将replacement转换为小写,直到遇到一个 \U 或者 \E
\l 表示将下一个字符变为小写
\U 表示将replacement转换为大写,直到遇到一个 \L 或者 \E
\u 表示将下一个字符变为大写
\E 用于终止由 \U 或者 \L启动的字母大小写转换
示例:
$ printf 'aabbcc\n' | sed 's/[a-Z]*/\U&/'
AABBCC
$ printf 'aabbcc\n' | sed 's/[a-Z]*/\u&/'
Aabbcc
$ printf 'AABBCC\n' | sed 's/[a-Z]*/\L&/'
aabbcc
$ printf 'AABBCC\n' | sed 's/[a-Z]*/\l&/'
aABBCC
$ printf 'AABBCC\n' | sed 's/\([A-Z]\)[A-Z]*/\L\1\E&/'
aAABBCC
替换标记
s 命令可以使用一个或多个的替换flag,如下:
- g 表示替换行内全部的匹配
- number(数字n) 表示只替换行内的第n个匹配
- w filename 表示可以将输出写入到指定的文件中
如下:
$ printf 'AABBCC\n' | sed 's/[a-Z]/123/4'
AAB123CC
$ printf 'AABBCC\n' | sed 's/[a-Z]/123/g'
123123123123123123
printf 'AABBCC\n' | sed -n 's/[a-Z]/123/g'
$ printf 'AABBCC\n' | sed -n 's/[a-Z]/123/gp'
123123123123123123
printf 'AABBCC\n' | sed -n 's/[a-Z]/123/gw 123.txt'
$ cat 123.txt
123123123123123123
我们可以使用 number(N)和g的组合来使得替换从第N处开始,而不仅仅是指替换第N处:
$ printf 'AABBCC\n' | sed 's/[a-Z]/123/4g'
AAB123123123
还有一个替换标记e,它可以将替换后的模式空间中的内容当做一个shell命令来执行,然后使用执行的结果替换模式空间的内容:
$ printf 'AABBCC\n' | sed -n 's/[a-Z]*/date/ep'
2018年 11月 18日 星期日 17:12:05 CST
$ printf 'AABBCC\n' | sed -n 's/[0-9]*/date/ep'
sh: dateAABBCC: command not found
$
上面示例中,第一个命令中,替换后的结果是date,所以最后输出的是date命令的执行结果,第二个命令中,由于替换后的结果是dateAABBCC,这不是一个shell命令,所有会有错误产生,同时模式空间也变为空了,因为没有结果输出。
其他常用的命令
q [exit-code] 退出sed脚本的执行
$ seq 12 20 | sed 3q
12
13
14
d 删除模式空间内容,然后开始下一个循环
$ seq 1 5 | sed 3d
1
2
4
5
$ seq 1 5 | sed 3,5d
1
2
p 打印模式空间到标准输出,该命令通常和 -n 选项一起使用
$ seq 1 5 | sed -n 3,5p
3
4
5
n 读入输入的下一行替换当前的模式空间内容,如果没有更多的输入读入则会退出sed,该命令通常用以跳过一些输入行,每隔 N 行进行处理,如下:
$ seq 1 10 | sed 'n;n;n;s/[0-9]*/HH/'
1
2
3
HH
5
6
7
HH
9
10
上述命令中,每隔4行进行替换处理。
当然我们可以通过另一种方式来达到上面的目标,GNU sed提供了一种 first~step 的地址语法:
$ seq 1 10 | sed '0~4s/[0-9]*/HH/'
1
2
3
HH
5
6
7
HH
9
10
上述命令表示从第0行开始,每4行执行一次替换操作。
{commands} 可以将一组命令放入到由大括号{}中去执行,这通常用以对某个特地的地址或者地址范围执行多个命令时使用:
$ seq 1 10 | sed -n '2,5s/[0-9]*/HHH/;p'
1
HHH
HHH
HHH
HHH
6
7
8
9
10
$ seq 1 10 | sed -n '2,5{s/[0-9]*/HHH/;p}'
HHH
HHH
HHH
HHH
可以通过上面的示例来了解使用 {} 和不使用 {}的区别。
不太常用的命令
= 打印当前输入行的行号
$ head -5 /etc/passwd | sed '='
1
root:x:0:0:root:/root:/bin/bash
2
bin:x:1:1:bin:/bin:/sbin/nologin
3
daemon:x:2:2:daemon:/sbin:/sbin/nologin
4
adm:x:3:4:adm:/var/adm:/sbin/nologin
5
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
y/source-chars/dest-chars/ 将source-chars中的字符替换为dest-chars对应位置的字符
$ echo hello world | sed 'y/abcdefghij/0123456789/'
74llo worl3
上述命令中,就是将输入行中字符a替换为数字0,字符b替换为数字1,字符c替换为数字2,依次类推。因此source-chars的长度和dest-chars的长度必须相同。
a text 或
a
text 在指定的输入行后添加内容
$ seq 1 3 | sed '2ahello world'
1
2
hello world
3
$ seq 1 3 | sed '2a\
> hello\
> world
> 3s/./HHH/'
1
2
hello
world
HHH
上面的示例都是在第二行后添加内容,如果需要添加多行内容,就要在每一行后使用一个反斜杠。如果不没有反斜杠,则表示添加内容的结束,如上的第二个命令。
i text 或
i
text 在指定的输入行之前插入内容
$ seq 1 3 | sed '2ihello world'
1
hello world
2
3
$ seq 1 3 | sed '2i\
> hello\
> world
> 3s/./HHH/'
1
hello
world
2
HHH
c text 或
c
text 替换指定行的内容
$ seq 1 10 | sed '2,9chello world'
1
hello world
10
$ seq 1 10 | sed '2,9c\
> hello\
> world'
1
hello
world
10
从上面的示例中可以看到这里的替换并不是指一行一行的替换,而是把内容整体替换了指定的行范围内的内容。
上面的三个命令添加 a、插入 i、替换 c、都可以使用 -e 选项将命令和内容分隔为2个参数,如下:
$ seq 1 3 | sed -e '2a\' -e hello
1
2
hello
3
$ seq 1 3 | sed -e '2i\' -e hello
1
hello
2
3
$ seq 1 10 | sed -e '2,9c\' -e hello
1
hello
10
r filename 读入文件filename的内容
$ seq 4|sed '2r/etc/passwd'
1
2
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
R filename 按顺序一行一行的读入文件内容
$ seq 4|sed '2R/etc/passwd'
1
2
root:x:0:0:root:/root:/bin/bash
3
4
$ seq 4|sed '2,4R/etc/passwd'
1
2
root:x:0:0:root:/root:/bin/bash
3
bin:x:1:1:bin:/bin:/sbin/nologin
4
daemon:x:2:2:daemon:/sbin:/sbin/nologin
w filename 将模式空间的内容写入到filename文件中
$ cat /etc/passwd | sed -n '/bash/w/tmp/test.txt'
$ cat /tmp/test.txt
root:x:0:0:root:/root:/bin/bash
hadoop:x:500:500:hadoop:/home/hadoop:/bin/bash
上面命令将passwd文件中包含bash字符串的记录写入到文件test.txt文件中。
使用多个命令
如果要在一个sed语句中使用多个sed的命令,有如下几种方法:
1. 每一个命令使用一行
$ seq 6 | sed '
> 2d
> 4s/./HH/
> 6d'
1
3
HH
5
2. 使用-e选项
$ seq 6 | sed -e '2d' -e '4s/./HH/' -e'6d'
1
3
HH
5
3. 使用分号
$ seq 6 | sed -e '2d ; 4s/./HH/ ; 6d'
1
3
HH
5
例外
有些命令是不能使用分号来分隔的,下面几种情形就不适合使用分号来分隔:
a,c,i(append/change/insert) 这三个命令之后所有内容都会被当做内容去添加、修改、插入:
$ seq 4 | sed '2ahello; 3d'
1
2
hello; 3d
3
4
可以看到分号后的命令也被当做内容添加了。
R,r,W,w 读写文件的时候,命令后面的部分都会被当做文件名
$ seq 2 | sed '1w hello.txt ; 2d'
1
2
$ ls -log
总用量 4
-rw-rw-r-- 1 2 11月 18 21:29 hello.txt ; 2d
$ cat hello.txt\ \;\ 2d
1
e (command execution)
$ seq 2 | sed '1e mkdir test ;2d'
sh: 2d: command not found
1
2
e 命令是把其后面直到行尾的所有内容都发送给shell来执行,上面的示例中,首先会执行mkdir test创建目录,然后会把2d当做shell命令来执行,此时自然就会报错。
s///[we] 使用s命令替换时使用了 w 或 e 替换标记,原因和上述使用 w 命令 和 e 命令是一样的。