我创建了一个名为“test”的测试文件,其中包含以下内容:
xxx
yyy
zzz
Run Code Online (Sandbox Code Playgroud)
我运行了命令:
(sed '/y/ q'; echo aaa; cat) < test
Run Code Online (Sandbox Code Playgroud)
我得到了:
xxx
yyy
aaa
zzz
Run Code Online (Sandbox Code Playgroud)
然后我跑了:
cat test | (sed '/y/ q'; echo aaa; cat)
Run Code Online (Sandbox Code Playgroud)
并得到:
xxx
yyy
aaa
Run Code Online (Sandbox Code Playgroud)
题
sed读取并打印,直到遇到带有 'y' 的行,然后停止。在第一种情况下,但不是第二种情况,cat 读取并打印其余部分。
有人可以解释这种行为差异背后的现象吗?
我还注意到它在 Ubuntu 16.04 和 Centos 6 中以这种方式工作,但在 Centos 7 中,这两个命令都没有打印“zzz”。
cuo*_*glm 22
当输入文件可查找(如从常规文件中读取)或不可查找(如从管道中读取)时,sed(和其他标准实用程序)的行为会有所不同(此链接中的阅读INPUT FILES部分)。
来自文档的引用:
当标准实用程序读取可查找的输入文件并在它到达文件结尾之前无错误地终止时,该实用程序应确保打开文件描述中的文件偏移正确定位在该实用程序处理的最后一个字节之后。
所以在:
(sed '/y/ q'; echo aaa; cat) < test
Run Code Online (Sandbox Code Playgroud)
sedq在到达 EOF 之前执行uit 命令,因此它zzz在行首留下文件偏移量,因此cat可以继续打印剩余的行(GNU sed 在某些情况下不符合 POSIX,见下文)。
并从文档继续:
对于不可搜索的文件,该文件的打开文件描述中的文件偏移状态是未指定的
在这种情况下,行为是未指定的。大多数标准工具,includesed会尽可能多地消耗输入。它读取通过yyy行,并且quit 不恢复文件偏移量,因此没有为cat.
GNUsed不符合标准,取决于系统的 stdio 实现和 glibc 版本:
$ (gsed '/y/ q'; echo aaa; cat) < test
xxx
yyy
aaa
Run Code Online (Sandbox Code Playgroud)
在这里,结果来自 Mac OSX 10.11.6、虚拟机 Centos 7.2 - glibc 2.17、Ubuntu 14.04 - glibc 2.19,它们在带有 CEPH 后端的 Openstack 上运行。
在这些系统上,您可以使用-u选项来实现标准行为:
(gsed -u '/y/ q'; echo aaa; cat) </tmp/test
Run Code Online (Sandbox Code Playgroud)
和管道:
$ cat test | (gsed -u '/y/ q'; echo aaa; cat)
xxx
yyy
aaa
zzz
Run Code Online (Sandbox Code Playgroud)
这会导致非常低效的性能,因为sed必须一次读取一个字节。部分输出strace:
$ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test'
...
[pid 5248] read(3, "", 4096) = 0
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "\n", 1) = 1
xxx
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "\n", 1) = 1
yyy
...
Run Code Online (Sandbox Code Playgroud)