sed 模式缓冲区
不知道你是否还记得我们在 sed 工作流程 里的那张流程图 ?我们在流程图里有提到,如果没有使用 -i
选项的话,sed 默认会把处理好的每一行的结果输出到输出流(模式是标准输出)。
因此,就有了下面这个 模仿 cat
命令输出文件内容的 空命令。
[www.twle.cn]$ sed '' data.txt
输出结果如下
1) 小明,23岁,北京大学 2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
哦,对了,忘记说了,我们的 data.txt
的内容改了,改成下面这个
1) 小明,23岁,北京大学 2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
其实,我们上面的说的那个 sed 默认会把处理好的每一行的结果输出到输出流 的说法并不严谨。
严谨的说法是 sed 默认会把模式缓冲区的文本输出到输出流。
模式缓冲区 我们在 sed 工作流程 里有提到,模式缓冲区 ( pattern buffer ) 用于存储经过 sed 处理的结果。
也就是说,有了模式缓冲区的概念之后,sed 的工作流程可以细化为
- 从输入流中读取一行数据,然后把它放到 模式缓冲区 中。
- 从模式缓冲区里读取数据,然后应用到 sed 命令集里的第一个命令,处理完后把数据写回模式缓冲区。
- 继续 [2] 直到 sed 命令集里的所有命令都应用完毕。
- 如果没有
-i
选项,则把模式缓冲区的文本输出到输出流。
因为这种工作流程机制,当我们把空命令改成 打印输出命令 p
后,运行结果就会出现重复的两行。
p
是
[www.twle.cn]$ sed 'p' data.txt
上面的命令运行结果如下
1) 小明,23岁,北京大学 1) 小明,23岁,北京大学 2) 小红,22岁,清华大学 2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学 4) 小王,22岁,清华大学
有了上面的描述,想必你对输出结果已经不感到奇怪了。p
命令输出一次,最后默认输出一次,当然就有两次了。
那问题来了,我们要如何禁用默认的输出呢?
sed 禁用默认模式缓冲区输出
好在 sed 的开发者足够聪明,既然有默认输出,那么就有禁止默认输出的选项。
这个默认的选项就是 -n
。
sed 程序命令行选项 -n
用于禁止 sed 默认输出模式缓冲区的数据,它的语法格式如下
sed -n [sed-command] [input-file]
例如,对于上面的空命令,要禁止默认输出,则命令为
[www.twle.cn]$ sed -n '' data.txt
运行结果为
可以看到没有任何输出。
对于上面的 p
命令输出,要禁止默认输出,则命令为
[www.twle.cn]$ sed -n 'p' data.txt
输出结果为
1) 小明,23岁,北京大学 2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
sed 输出指定的行
经过上面的学习,我们终于知道如何禁用 sed 程序的默认输出了。
但问题随之又来了,一旦使用了 p
,它默认就会把每行给打印出来,如果我们只想输出某些行要怎么做呢?
比如我们只想输出第三行 3) 小李,25岁,斯坦福大学
要怎么做呢?
别担心,sed 早就为我们解决了这个问题了。它们把 p
命令给改造了下,支持 前缀数字 用于输出指定行。
前缀数字 p 命令的语法格式为
[line-number]p
[line-number]
是可选的,如果不指定,则默认输出所有的行。
[line-number]
是行号的意思,如果要输出第几行,直接把 [line-number]
指定为行号即可,例如要输出第 2 行,那么命令就是
2p
需要注意的是 [line-number]
行号是从 1
开始的,第一行是 1
,第二行是 2
,以此类推。
如果指定的行号超出了输入源中的行总数,那么就不会输出,因为行不存在啊,所有什么输出都没有。
范例 1
对于我们提供的数据,如果要输出第一行,直接使用 1p
即可,命令如下
[www.twle.cn]$ sed -n '1p' data.txt
输出结果如下
1) 小明,23岁,北京大学
范例 2
如果指定的行不存在,例如 6
行则什么输出也没有
[www.twle.cn]$ sed -n '6p' data.txt
输出结果如下
范例 3
如果指定的行号小于 1,则会报错
[www.twle.cn]$ sed -n '0p' data.txt
运行报错 命令不存在
-bash: [www.twle.cn]$: command not found
输出指定范围的行
上面我们介绍了如何使用 [line-number]p
语法输出指定的行。除了能支持单行输出外,sed 还支持多行输出。
其中一种多行输出就是输出指定指定范围的行 [n,m] p
。
[n,m] p
语法,准确的说是 n,m p
语法用于输出 n
到 m
之间的所有行,包括 n
和 m
自身。
需要注意几点
- 如果
n
大于等于m
那么只会输出第n
行。 - 如果
m
大于总行数,则只会输出到所有行结束。 - 如果
n
大于总行数,那么什么都不会输出。 - 如果
n
为0
则什么都不输出。 - 如果
m
为0
则输出第n
行。 m
与p
之间可以有空格也可以没有空格,但n
和m
之间必须有 逗号(,
)。
范例 1
输出第 2 到第 6 行。因为实际只有 4 行,所以只会输出 2 3 4 行。
[www.twle.cn]$ sed -n '2,6p' data.txt
运行结果如下
2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
范例 2
如果 n 小于等于 m 则只会输出第 n 行
[www.twle.cn]$ sed -n '4,2p' data.txt
输出结果如下
4) 小王,22岁,清华大学
范例 3
如果 n
等于 0 则什么都不输出
[www.twle.cn]$ sed -n '0,2p' data.txt
输出结果如下
只输出文件最后一行
如果我们只想输出最后一行,那么上面我们学习到的语法是没有任何机会的。
sed 没有参数来指示到底有多少行。它是按行读取的,没有读到文件结束符,它就不会结束。
为了能够输出最后一行,sed 提供了 美元符号 $
用于表示最后一行。
因此输出最后一行的语法格式为 $ p
。 中间的空格有没有无所谓,为了清晰,建议保留空格。
范例
输出源文件的最后一行
[www.twle.cn]$ sed -n '$ p' data.txt
运行结果如下
4) 小王,22岁,清华大学
输出指定行到文件结束的所有行。
上面我们提到 美元符号 $
用于表示最后一行。
$
不仅能够用于输出最后一样,还能够用在我们上面学习的 指定范围里面,用于输出指定行到文件结束的所有行。
输出指定行到文件结束的所有行的语法格式如下
n,$ p
其中 n
表示从指定行开始。
例如,如果要输出从第二行开始到文件结束的所有行,可以使用下面的语法
2,$ p
范例
输出从第二行开始到文件结束的所有行
[www.twle.cn]$ sed -n '2,$ p' data.txt
运行结果如下
2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
输出指定行 n 开始的连续 m 行
上面我们学习了如何使用 逗号(,
) 语法来输出指定返回的行。学习了如何使用 $
符号来输出最后一行,学习了如何使用 $
符号输出指定 n 行开始到文件结束的所有行。
其实,sed 还支持其它的 符号,例如 加号( +m
) 用于表示接下来的连续 m+1 行。
如果要输出指定行 n 开始的连续 m+1 行,可以使用下面的语法
n,+m p
例如要输出从第二行开始,连续的 3+1 行,sed 命令如下
2,+3p
注意:
- 如果 m 为
0
那么只会输出第 n 行。 - 如果 m 大于 0 那么会输出 [n,m+n] 行。
- 如果 n+m 的值大于总行数,那么会输出到文件末结束。
n,+m p
语法不适用于苹果电脑自带的sed
程序。会报错误sed: 1: "2,+2 p": expected context address
范例
输出从第二行开始,连续的 3+1 行
[www.twle.cn]$ sed -n '2,+3 p' data.txt
运行结果如下
2) 小红,22岁,清华大学 3) 小李,25岁,斯坦福大学 4) 小王,22岁,清华大学
范例 2
使用 n,+m p
语法只输出第 n 行,只要将 m
设置为 0
即可
[www.twle.cn]$ sed -n '2,+0 p' data.txt
输出结果如下
2) 小红,22岁,清华大学
从第 n 行开始隔 m 行输出
GNU Sed 程序同时还提供了 波浪线 ( ~
) 符号用于隔 m 行输出。例如 ~2
隔 2 行输出。
有了 隔行 输出语法,那么我们就可以实现从第 n 行开始隔 m 行输出,语法格式如下
n~m p
上面的语法表示从第 n 行开始,每隔 m 行输出,也就是输出 n, n+m, n+2m, n+3m 等等行。
例如 1~2p
则是输出 1
,3
,5
行。
范例
从第一行开始,每隔两行输出
[www.twle.cn]$ sed -n '1~2p' data.txt
输出结果为
1) 小明,23岁,北京大学 3) 小李,25岁,斯坦福大学