Cygwin 2.9.0 cat/tac 命令在通过管道传输到 grep -q -m1 时对大文件失败

Cygwin 2.9.0 cat/tac 命令在通过管道传输到 grep -q -m1 时对大文件失败

我在 Windows 10 Pro x64 上使用 Cygwin x64 2.9.0 时发现一些奇怪的行为。我尝试运行的命令如下:

tac <file> | grep -q -m1 -F "literal string"

上述命令对我向其发送的所有小文件(小文件指 <= 15kB)均成功执行。如果 的最后一次出现位置literal string靠近文件开头(例如,literal string出现在文件顶部附近,而不在其他地方),则该命令也会成功执行。最后,当未将 { -q-m1} 标志传递给grep命令时,该命令也会成功执行。

但是,当文件大小约为 680kB 时,如果literal string出现在文件末尾附近,则tac命令会将“tac: write error”打印到 STDERR。尽管出现此错误,但命令似乎已成功,将匹配的行打印到输出(省略标志时-q)并从 获得适当的返回值grep

进一步的测试表明,使用时也会出现同样的错误cat,只不过literal string必须出现在文件开头附近才会产生错误,而产生的错误是“cat:写入错误:设备上没有剩余空间”。

请注意,仅当至少将 { -m1, -q} 选项之一传递给grep命令、匹配项靠近文件的第一个处理行(因为cat它靠近开头,因为tac它靠近结尾)并且文件很大时,才会发生这种情况。

我运行了该df命令,它报告 Cygwin 驱动器上有 14 MB 可用空间,实际磁盘上有 60 GiB 可用空间。我知道我可以简单地将 STDERR 重定向到 NUL 设备,但这似乎是一种不靠谱的解决方法。有人知道如何正确修复这个问题吗?

开始编辑

我发现另一份报告2017 年 5 月出现了同样的错误,但没有提出解决方案。另一篇帖子的 OP 确实表示他认为这是管道缓冲区大小限制(可能在 Windows 上,也可能在 Cygwin 中)。

答案1

我发现了一些解决方法。只需更改命令:

tac <file> | grep -q -m1 -F "literal string"

至以下之一:

bash -c "tac <file> | grep -q -m1 -F 'literal string'"
stdbuf -o L tac <file> | grep -q -m1 -F "literal string"

我认为第一种方式有效是因为它使用了 Linux 管道,第二种方式是因为它强制tac命令输出为行缓冲。这两种方式都可以消除错误。

由于这有效,我猜问题是grep一旦找到第一个匹配项,它就会停止处理输入缓冲区,但tac会继续处理输入。一旦缓冲区已满(可能是 64kiB),缓冲区就会阻塞并tac退出并出现指定的错误。但是,由于tac在崩溃之前成功处理了我关心的行,所以一切都按预期运行。

对这些选项进行计时表明调用bash是更快的选项。这可能是因为使用 Linux 管道,一旦找到第一个匹配项,tac就能够立即返回。grep

相关内容