曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5346|回复: 1
打印 上一主题 下一主题

使用 N 命令读取下一行到 pattern space

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2012-1-31 02:17:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一般情况下,sed 是“行”的流编辑器,有些时候,也有多行操作的需要。比如,可能希望将一个文本中的所有换行符去掉,你可能会想到使用下面的语句:
$ sed 's/\n//' filename
但这么做的结果并不像想象那样,看起来似乎并不凑效。这是因为,当 sed 在处理每一行时,会将该行读入到 pattern space (这个概念可参考:http://www.groad.net/bbs/read.php?tid-6021.html),而其后的换行符不会被读入,所以这样像上面的做法,并不会起到消除换行的目的。

然而,sed 里的 N 命令可以用来读取文件中的下一行到 pattern space 中,被 N 命令所读入的行附加在前一行的后面,两行之间还添加了一个换行符以作分隔,相当于:
FirstLine\nSecondLine

下面是一个供测试用的文本内容:
# cat test.txt
Linux Sysadmin
Databases - Oracle, mySQL etc.
Databases - Oracle, mySQL etc.
Security (Firewall, Network, Online Security etc)


Storage in Linux
Website Design
Website Design
Windows- Sysadmin, reboot etc.
上面的文本中,有两个连续的空行。

利用上面的这个文本,首先证明 sed 每次所读入的一行内容并不包含换行符(\n):
# sed '{
N
s/\n/ @ /
}' test.txt
Linux Sysadmin @ Databases - Oracle, mySQL etc.
Databases - Oracle, mySQL etc. @ Security (Firewall, Network, Online Security etc)
@
Storage in Linux @ Website Design
Website Design @ Windows- Sysadmin, reboot etc.
上面,花括号用来包含命令的组合,其中有两个命令,一个是 N 命令,用来读取下一行内容到 pattern space ;另一个是 s/\n/ @ / ,用来将换行符 \n 替换为 “ @ ”  。从输出的第一行(Linux Sysadmin @ Databases - Oracle, mySQL etc.)可以看到,"etc." 的后面并没有 “ @ ” 符号,也就是说被读入到 pattern space 中的 “Databases - Oracle, mySQL etc.” 这一行后并没有换行符,因此得出一个结论:通常,sed 读入到 pattern space 的一行内容并不包括换行符。所以,这也说明了为什么上面所说的 sed 's/\n//' filename 语句不能奏效的原因。


从上面的例子还可以看到两个空行被合并成了一行。


在《使用 = 命令打印行号
》里演示了用 = 命令来打印行号,我们这里也用来试试看:
# sed '/./=' test.txt
1
Linux Sysadmin
2
Databases - Oracle, mySQL etc.
3
Databases - Oracle, mySQL etc.
4
Security (Firewall, Network, Online Security etc)


7
Storage in Linux
8
Website Design
9
Website Design
10
Windows- Sysadmin, reboot etc.
上面 sed 命令中 /./ 表示匹配非空行。从输出的结果可以看到,行号和内容各为一行,这有时会让人觉得缺少美观,我们可能更希望看到下面的展现形式:
1 Linux Sysadmin
2 Databases - Oracle, mySQL etc.
这里,我们同样可以利用 N 命令来实现,如:
# sed '/./=' test.txt |sed 'N; s/\n/ /'
1 Linux Sysadmin
2 Databases - Oracle, mySQL etc.
3 Databases - Oracle, mySQL etc.
4 Security (Firewall, Network, Online Security etc)

7 Storage in Linux
8 Website Design
9 Website Design
10 Windows- Sysadmin, reboot etc.
现在排版变得美观了。

如果我们希望去除掉文本中的两个连续空行(多个连续空行也同样适用),那么可以如下使用:
# sed '/^$/{N
/^\n$/d
}' test.txt
Linux Sysadmin
Databases - Oracle, mySQL etc.
Databases - Oracle, mySQL etc.
Security (Firewall, Network, Online Security etc)
Storage in Linux
Website Design
Website Design
Windows- Sysadmin, reboot etc.
上面命令中,^$ 表示空行,而 /^\n$/d 表示开头到末尾的中间只有一个换行符,而这个换行符是由 N 命令所添加,因此它表示两个空行。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
沙发
 楼主| 发表于 2012-1-31 14:13:25 | 只看该作者

N 与 $d 的命令组合

如果对一个文本执行 sed 'N;$d' 会发生什么样的情形?

假设一个文本里有任意行(或奇数行或偶数行),那么先来看一下单 $d 命令的情况:

1. 含有偶数行的文本
[root@qunet sed]# cat temp.txt
hello world
hello linux
hello shell
hello groad
使用下面命令:
[root@qunet sed]# sed '$d' temp.txt
hello world
hello linux
hello shell
最后一行被删除。

2. 含有奇数行的文本
[root@qunet sed]# sed '$d' temp.txt
hello world
hello linux
hello shell
hello groad
同样,最后一行也被删除。

下面使用 'N; $d' 这两个命令组来测试上面的情况,会看到不一样的情况:

1. 含有奇数行的文本
[root@qunet sed]# sed 'N; $d' temp.txt
hello world
hello linux
hello shell
hello groad
hello google
整个文本完整输出,最后一行没有被删除。

2. 含有偶数行的文本
[root@qunet sed]# sed 'N; $d' temp.txt
hello world
hello linux
只输出两行。

这里的原因是:在开始执行 sed 命令时,先将第一行(hello world)读入到 pattern space 中,然后接着执行 'N' 命令,于是第二行(hello linux)也被读入到 pattern space 中,这两行由换行符隔开。再接着,执行 $d 语句,该语句表示删除掉最后一行,当执行该语句时,sed 会测试一下文本是否到了末尾,如果没有,那么会输出 pattern space 中的第 1 行和第 2 行内容,完后就将第 3 行读入到 pattern space,接着又执行 N 命令读入第 4 行到 pattern space ,当再次执行到 $d 命令时,sed 会接着测试文本是否到末尾,这时,假设整个文本总共就只有 4 行,那么此时已经到文本末尾,$d 命令会将 pattern space 中的内容删除,因此最后结果输出只有第一和第二行。但是,如果整个文本有 5 行,那么,在 sed 再进行一次命令组循环处理时,'N' 命令无法再读入下一行,命令执行失败,因此 $d 命令将不会被执行,所以 pattern space 中的第 5 行(hello google)不会被删除掉 ,故而最后仍然会将该行输出。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-15 16:49 , Processed in 0.060206 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表