=== 目录 ===
简介
返回目录
本文介绍了sed
命令的基本规则和基础用法。如果希望了解sed
命令的高级用法,请搜索其它教程。据说完整地介绍sed
需要一本书。
sed
是 Stream EDitor 的缩写。sed
的好处是,不需要像vim
那样打开文件后才能编辑,在shell
脚本中,如果要对文本做一些简单的处理(比如插入、删除文本行、替换),可以用sed
命令实现。
sed 命令的执行过程
返回目录
执行 sed
命令需要告诉 sed
两样东西:sed script
和待处理的文本。sed script
包含一条或多条sed command
(sed
命令)。开始执行后,sed
命令一行一行地处理给定的文本,对每一行,依次应用给定的sed
命令。
# 把单词 'hello' 替换成 'world'
$ echo 'hello' | sed 's/hello/world/'
world
这个示例中,sed命令
只有一条:s/hello/word/
,待处理的文本也只有一行:word
。命令的作用:用word
替换hello
。
sed
确定sed script
和待处理文本的逻辑是:
- 用
-e
指定sed命令
,或者用-f
指定`sed 脚本的文件命名 - 如果这两个选项都没有,那第一个非
sed
选项参数被解析为sed 命令
。 - 其余非选项参数被人为是待处理的文本文件的名字。
- 如果没有给定待处理的文件名,或这文件名为
-
,则从标准输入 stdin
读取待处理文本。非sed选项参数是指第一个不以-
开头的参数。
可以同时使用-e
和-f
,可以多次使用-e
和-f
。
下面是相应的示例代码。
# 用含有三行带编号的 hello 的文件做测试
$ cat tri-hello.txt
1 hello
2 hello
3 hello
# 把每一行的 hello 替换为 "world"
$ sed -e 's/hello/world/' tri-hello.txt
1 world
2 world
3 world
# 如果不用 -e 和 -f 选项,第一个非选项参数就是要执行的 sed 命令
$ sed 's/hello/world/' tri-hello.txt
1 world
2 world
3 world
# 用 -e 选项,可以把要执行的 sed (子)命令放在后面,例如:
$ sed tri-hello.txt -e 's/hello/world/'
1 world
2 world
3 world
# 如果省略文件名,sed命令会从标准输入获取要处理的文本
$ echo 'word' | sed 's/hello/world/'
world
$ cat tri-hello.txt | sed 's/hello/world/'
1 world
2 world
3 world
sed
命令开始执行后,其处理步骤简要概括如下:
- 从
input stream
中取出一行文本,放入一个叫pattern sapce
的地方, - 把 sed script 里的命令逐个应用到这一行文本上,
- 如果没有指定
-n
选项,把pattern space
里的内容(就是处理后的文本)输出到output stream
中,否则转至步骤 4. - 最后清空
pattern space
; - 取出下一行文本,继续下一个处理循环。
完整的处理循环参考 gnu 关于 sed 的在线文档中Execution-Cycle部分的内容。
-i --in-place选项
返回目录
,使用-i直接修改文件内容,不输出。
$ for i in {1..5}; do echo $i hello; done > th.txt
# sed默认把结果打印到屏幕上
$ sed 's,he,H-,' th.txt
1 H-llo
2 H-llo
3 H-llo
4 H-llo
5 H-llo
# 使用 -i 选项,直接修改文件
$ sed 's,he,H-,' -i th.txt
$ cat th.txt
1 H-llo
2 H-llo
3 H-llo
4 H-llo
5 H-llo
# 使用重定向写入原来的文件不靠谱,因为th.txt既做为输入又作为输出的时候,sed命令没有对最终结果做保证
# 这是我在CentOS7虚拟机上做的试验,命令卡死了
$ sed 's,he,H-,' > th.txt
^C
# Ctrl-C结束命令后文件是空的。
$ cat th.txt
$ file th.txt
th.txt: empty
# MSYS上试验的,直接是空文件
$ sed 's,he,H-,' th.txt
1 H-llo
2 H-llo
3 H-llo
4 H-llo
5 H-llo
$ sed 's,he,H-,' th.txt > th.txt
$ cat th.txt
$ file th.txt
th.txt: empty
-r -E 使用扩展的正则表达式
返回目录
sed
的替换命令s
默认使用的是BRE(基本正则表达式),使用-r
选项,将支持ERE(扩展的正则表达式)。就跟grep
和grep -E
一样。
选择要处理的文本行
返回目录
大多数sed命令
都可以加上一个address
(地址)前缀。省略address
的时候,命令会应用在每一行上,指定了address
,命令就只在address
选定的文本行上起作用。可以把address
理解为条件判断,sed
命令只处理符合条件的文本行。
最简单的address
是直接指定行号,行号从 1 开始编号。第一行1
,第三行到第五行1,5
。示例如下:
# 最简单的`address`就是行号。行号从 1 开始编号
# 替换第一行
$ cat tri-hello.txt | sed '1s/hello/----/'
1 ----
2 hello
3 hello
# 替换第二行
$ cat tri-hello.txt | sed '2s/hello/----/'
1 hello
2 ----
3 hello
# 指定超过返回的行号,不会报错
$ cat tri-hello.txt | sed '5s/hello/----/'
1 hello
2 hello
3 hello
# 更常用的是指定行号范围,把“hello”换成“------”
$ cat ten-hello.txt | sed '2,4s/hello/------/'
1 hello
2 ------
3 ------
4 ------
5 hello
6 hello
7 hello
8 hello
9 hello
10 hello
sed
的常用命令
返回目录
除了替换命令s
,sed
提供了其它文本处理命令。
command | description |
---|---|
p | 打印当前行 |
i | 在当前行前面插入文本 |
a | 在当前行后面插入文本 |
d | 删除当前行 |
s | 对当前行作文本替换 |
### 测试用的文本,head 命令的 -n 参数在不同的示例中会取不同的值
$ nl /etc/passwd | head -n 5
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
### 打印命令:p,(联想单词 print,帮助记忆)
# 一般和 -n 选项结合使用,下面的命令打印文件的第3行到第8行
$ nl /etc/passwd | head -n 10 | sed -n '3,7p'
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
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
### 插入命令:i,(联想单词 insert 帮助记忆)
# 格式是 i<text>,比如在第一行的前面插入一行内容'hello'
$ nl /etc/passwd | head -n 4 | sed '1ihello'
hello
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
### 插入命令:a,(联想单词 append)
# 格式是 a<text>,比如在第一行的后面插入一行
$ nl /etc/passwd | head -n4 | sed '1ahello'
1 root:x:0:0:root:/root:/bin/bash
hello
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
### 删除命令:d (delete)
$ nl /etc/passwd | head | sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 news:x:9:13:news:/etc/news:
# 可以看到,2,3,4,5 行都被删除了
命令s
是常用命令,所以单列一节,详细学习一下。
替换命令 s
返回目录
命令s
可以看作单词substitute
(替换)或search-and-replace
(搜索并替换)的首字母。而且,sed
的s
命令和vi
里的:s
命令很相近。
回到最开始的例子,找找感觉。
$ echo hello | sed 's/hello/world/'
world
完整的语法是s/regexp/replacement/g
它的作用是把符合正则表达式regexp
的字符串替换为replacement
对应的字符串。后面的g
是可选的。
第一,选项g
(待续)
第二,分隔符可以任意选择,只是习惯一般上使用/
。紧跟在字母s
后的第一个字符就是分隔符,比如,使用下划线_
做分隔符:
# 用下划线做分隔符:
$ echo hello | sed 's_hello_world_'
world
# 惯例,使用斜杠做分隔符;但在某些情况下换个字符做分隔符可以提高可读性,
# 比如替换字符串中本身含有斜杠
$ echo '04/16' | sed 's_/_-_'
04-16
# 如果非得用斜杠(/)做分分隔符,就需要用反斜杠(\)对不是分隔符的斜杠转义:
$ echo '04/16' | sed 's/\//-/'
04-16
第三,正则表达式语法基本语法(待续)
第四,BRE
和ERE
(待续)
第五,正则表达式中的反向引用(待续)
第六,有些情况,用命令awk
实现更方便(待续)
address
的语法
Address | Description |
---|---|
n | A line number where n is a positive integer. |
$ | The last line. |
/regexp/ | Lines matching a POSIX basic regular expression. Note that the \regular expression is delimited by slash characters. Optionally, the regular expression may be delimited by an character, by specifying the expression with \cregexpc, where c is the alternate character. |
addr1,addr | A range of lines from addr1 to addr2, inclusive. Addresses may be any of the single address forms above. |
first~step | Match the line represented by the number first, then each subsequent line at step intervals. For example 1~2 refers to each odd numbered line, 5~5 refers to the fifth line and every fifth line thereafter. |
addr1,+n | Match addr1 and the following n lines. |
addr! | Match all lines except addr, which may be any of the forms above |
学习中想到的一些问题
-
sed
和其它命令的比较可以编写复杂的
sed
脚本,sed
可以实现相当复杂的文本处理任务,但通常只用sed
做一些简单的文本处理任务,就是,在命令行中提供一两条简单sed
命令,而不是使用复杂的sed脚本
。It is most often used for simple, one-line tasks rather than long scripts.
把
sed
看作文本处理命令,相关命令有cat
、sortuniq
、cut
、paste
、join
、common
、diff
、patch
、tr
。这些文本处理命令的功能有重合的地方,
sed
的用法更复杂,功能也更强大,常见的文本处理任务中,除了文本替换其它任务用别的命令也能实现,而且用起来更简单些。a. 输出文件内容:
cat
命令,或者sed
的-n
选项+p
命令
b. 文本替换- 一般的替换: 只有
sed
的s
命令 - 大小写转换:
tr
命令,或者sed
的y
命令
c. 正则过滤:
grep
命令,sed
的address
+p
命令awk
是比sed
功能更强大的文本处理命令,也更复杂。awk
适合处理表格数据。 - 一般的替换: 只有
-
-e
第一个非选项参数就是sed 命令
,那为什么还要提供-e
选项?能去掉-e
选项吗?- 加上
-e
可以把sed 命令
的放在其它位置,而不仅仅是只限定在第一个非选项参数。 -e
选项可以出现多次,从而一次性提供多个sed命令
- 加上
-
i 和 a 命令
为什么a和i的功能重复的命令?能去掉其中的一个吗?
不是完全重复,在第一行前面插入一行文本,用 a 就没法实现;在最后一行的后面插入文本,用 i 就没法实现。
用sed
的常见任务
- 取文件 ten-hello.txt 的第 6到第9行的内容。
# 用 sed 命令,
$ sed '6,9p' -n ten-hello.txt
6 hello
7 hello
8 hello
9 hello
# 另一种方式,用 head + tail 命令
$ head -n 9 ten-hello.txt | tail -n $((9-6+1))
6 hello
7 hello
8 hello
9 hello
附
相关测试文件
生成这些文件的命令
# 生成测试文件
$ nl /etc/passwd | head -n 12 > hp.txt
$ for i in {1..10};do echo $i hello ;done > ten-hello.txt
$ head -n 3 hello.txt > tri-hello.txt
hp.txt 文件内容
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
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 news:x:9:13:news:/etc/news:
11 uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
12 operator:x:11:0:operator:/root:/sbin/nologin
ten-hello.txt 文件的内容
1 hello
2 hello
3 hello
4 hello
5 hello
6 hello
7 hello
8 hello
9 hello
10 hello
参考链接
The Linux Command Line
鸟哥的 Linux 私房菜
$ man sed
或者在线手册