sed(stream editor)命令是一个面向字符流的非交互式编辑器,是按行来处理文本内容的;它还能配合管线来使用,非常高效
我之所以想学会使用这个命令,是因为:当同一个长文本每次执行只需要修改一个小地方时,使用sed能直接对文本操刀,避免使用复杂的python文本生成来覆盖原文本,配合bash脚本使用简直美滋滋
需要注意的另一个点是:可以先使用sed确保命令本身没问题,再使用-i
选项直接修改文本(不使用-i
选项,执行sed命令后原文本文件不会被修改)
sed [选项] {动作或脚本命令, 如果没有其他脚本的话} 文本文件名
1. 常用选项
-n
: 安静模式,只打印被修改了的行-e <script>
: 使用选项中指定的script来处理输入的文本文件-f <script文件>
: 使用选项中指定的script文件来处理输入的文本文件-r
: 在动作中使用扩展的正则表达式(默认是基础正则表达式,比如使用限定符会无效等)-i
: 直接对文本文件操刀,而不是打印结果到终端--follow-symlinks
: 保持软连接文件的连接,硬连接没招儿--version
: 打印版本信息
2. 常用动作
p
: 列出选中的行a
: 在指定行后面追加文本i
: 在指定行前面插入文本d
: 删除指定的行c
: 用新文本替换掉选中的行s/正则表达式或指定文本/新文本/
: 用新文本替换匹配到的内容,最后加g
表示全部替换,否则只替换每一行的第一个匹配内容。而实际上/
在这里只是分隔符,s
后面紧接着的就是分隔符,可以用别的符号代替,例如#
或@
等(亲测)。
一般脚本命令前面会加指定行区间,比如加2
表示只对第2行进行操作,2,$
表示对第2行至最后一行进行操作等
3. 实例
原文本内容:
ubuntu@ubuntu:~$ cat test
hello linux!
i have 10 apples.
i have no idea for this file.
233333
增加行
ubuntu@ubuntu:~$ sed '3a\add newline after 3rd line.' test
hello linux!
i have 10 apples.
i have no idea for this file.
add newline after 3rd line.
233333
ubuntu@ubuntu:~$ sed '3i\add newline before 3rd line.' test
hello linux!
i have 10 apples.
add newline before 3rd line.
i have no idea for this file.
233333
ubuntu@ubuntu:~$ sed '1,2a\add newline after 1st and 2nd line.' test
hello linux!
add newline after 1st and 2nd line.
i have 10 apples.
add newline after 1st and 2nd line.
i have no idea for this file.
233333
删除行
ubuntu@ubuntu:~$ sed '2d' test
hello linux!
i have no idea for this file.
233333
ubuntu@ubuntu:~$ sed '2,$d' test
hello linux!
ubuntu@ubuntu:~$ sed -r '/[0-9]+/d' test
hello linux!
i have no idea for this file.
打印行
ubuntu@ubuntu:~$ sed -r -n '/[0-9]+/p' test
i have 10 apples.
233333
替换整行
ubuntu@ubuntu:~$ sed '/^i/c replace' test
hello linux!
replace
replace
233333
ubuntu@ubuntu:~$ sed '1c replace heihei' test
replace heihei
i have 10 apples.
i have no idea for this file.
233333
替换行内的部分内容(含大小写转换)
ubuntu@ubuntu:~$ sed -r '2s/[0-9]+/20/g' test
hello linux!
i have 20 apples.
i have no idea for this file.
233333
ubuntu@ubuntu:~$ sed -r 's/^[a-z]/\u&/g' test
Hello linux!
I have 10 apples.
I have no idea for this file.
233333
ubuntu@ubuntu:~$ sed -r 's/\b[a-z]/\u&/' test
Hello linux!
I have 10 apples.
I have no idea for this file.
233333
ubuntu@ubuntu:~$ sed -r 's/\b[a-z]/\u&/g' test
Hello Linux!
I Have 10 Apples.
I Have No Idea For This File.
233333
ubuntu@ubuntu:~$ echo 'abcbc' | sed 's@b@B@'
aBcbc
【注】说一下\u
的意思:\u
是转换后面的内容第一个字母为大写,\U
是全部为大写直到遇到\E
为止。同理,\l
和\L
的作用类似,只是把大写变为小写。
ubuntu@ubuntu:~$ sed -r 's/[a-z]/\U&/g' test
HELLO LINUX!
I HAVE 10 APPLES.
I HAVE NO IDEA FOR THIS FILE.
233333
ubuntu@ubuntu:~$ sed -r 's/[a-z]/\U&/' test
Hello linux!
I have 10 apples.
I have no idea for this file.
233333
连续命令
sed -e 命令脚本1 -e 命令脚本2 -e 命令脚本3 文本文件名
ubuntu@ubuntu:~$ sed -e '1a\add oneline' -e '$d' test
Hello linux!
add oneline
I have 10 apples.
I have no idea for this file.
【注】上面的实例都是没有直接操刀原文本文件的,也就是说,上面实例的命令执行完毕之后原文本文件并没有被修改。实际应用中应该明白到随意直接修改原文本是很危险的,所以应该在确保命令没有问题之后,再在信任的sed命令中加上-i
选项,效果如下所示:
ubuntu@ubuntu:~$ sed -ir 's/[a-z]/\U&/' test
ubuntu@ubuntu:~$ cat test
Hello linux!
I have 10 apples.
I have no idea for this file.
233333
修改软连接文件
加上--follow-symlinks
这个选项即可。具体解释如下:
我们知道,硬连接的两个文件的inode的值是一样的(文件类型是-
),而软连接的两个文件的inode是不一样的(连接文件的类型是l
)。但是在使用sed -i
命令修改了连接文件内容后,被修改的软连接文件就会变成普通文件(-
),硬连接文件的inode值就会被改变,像这样:
- 软连接文件
ubuntu@ubuntu:~$ ll -i testa testb
7866189 -rw-rw-r-- 1 ubuntu ubuntu 12 1月 12 20:29 testa
7864805 lrwxrwxrwx 1 ubuntu ubuntu 9 1月 12 20:39 testb -> testa
ubuntu@ubuntu:~$ sed -i '2c replace' testb
ubuntu@ubuntu:~$ ll -i testa testb
7866189 -rw-rw-r-- 1 ubuntu ubuntu 12 1月 12 20:29 testa
7866188 -rw-rw-r-- 1 ubuntu ubuntu 13 1月 12 20:41 testb
- 硬连接文件
ubuntu@ubuntu:~$ ll -i testa testb
7866189 -rw-rw-r-- 2 ubuntu ubuntu 12 1月 12 20:29 testa
7866189 -rw-rw-r-- 2 ubuntu ubuntu 12 1月 12 20:29 testb
ubuntu@ubuntu:~$ sed -i '2c replace' testb.txt
ubuntu@ubuntu:~$ ll -i test test
7866189 -rw-rw-r-- 1 ubuntu ubuntu 12 1月 12 20:29 testa
7864805 -rw-rw-r-- 1 ubuntu ubuntu 13 1月 12 20:38 testb
实际上,只要使用sed -i
命令修改了文件内容,那么文件的inode值一定会变,像这样:
- 普通文件
ubuntu@ubuntu:~$ ll -i testc
7866191 -rw-rw-r-- 1 ubuntu ubuntu 10 1月 12 20:31 testc
ubuntu@ubuntu:~$ sed -i '2c replace' testc
ubuntu@ubuntu:~$ ll -i testc
7864805 -rw-rw-r-- 1 ubuntu ubuntu 13 1月 12 20:42 testc
所以,修改了硬连接文件的文件内容又想保持连接,没招儿(有人说-c
选项可以,但我试了,不行)。
而要使得修改后的软连接文件还能保持连接,就需要加上--follow-symlinks
这个选项,不过这个时候就是原文件的inode值变了(原文件被修改了),像这样:
ubuntu@ubuntu:~$ ll -i testa testb
7866189 -rw-rw-r-- 1 ubuntu ubuntu 12 1月 12 20:29 testa
7866188 lrwxrwxrwx 1 ubuntu ubuntu 9 1月 12 20:44 testb -> testa
ubuntu@ubuntu:~$ sed -i --follow-symlinks '2c replace' testb
ubuntu@ubuntu:~$ ll -i testa testb
7866190 -rw-rw-r-- 1 ubuntu ubuntu 13 1月 12 20:45 testa
7866188 lrwxrwxrwx 1 ubuntu ubuntu 9 1月 12 20:44 testb -> testa
感谢 @Qingqing1992 的留言!
批量替换
sed -i 's/旧字符串/新字符串/g' `grep 旧字符串 -rl 路径`
sed -i 's/旧字符串/新字符串/g' *
-
在指定路径下递归匹配所有含有旧字符串的文件,执行sed,例如:
sed -i 's/218.192.160.64/0.0.0.0/g' `grep 218.192.160.64 -rl ./`
-
对当前目录下所有文件执行sed,当前目录不允许有不是文件的文件(例如目录)
sed -i 's/218.192.160.64/0.0.0.0/g' *
- 更多用法,可以使用
man sed
命令来查看 - 更新(2019-01-12):修改软连接文件
- 更新(2019-03-07):批量替换