我使用python脚本对zgrep
进行系统调用,并且只使用-m1
选项打印第一个结果。在
#! /usr/bin/env python2.7
import subprocess
print subprocess.check_output("zgrep -m1 'a' test.txt.gz", shell=True)
在大文件(+2MB)上运行脚本时,将生成以下错误。在
^{pr2}$但是,如果我复制python抱怨的命令并直接在shell中运行它,它就可以正常工作了。在
> zgrep -m1 'a' test.txt.gz
0000000 8c82 524d 67a4 c37d 0595 a457 b110 3192
在shell中手动运行后,该命令的退出状态为0
,表示成功。Python表示该命令退出时返回错误代码2
。在
> echo $?
0
下面是如何制作一个示例测试文件来重现错误。它创建一个100000行的随机变量的十六进制文件,并使用gzip
对其进行压缩。在
cat /dev/urandom | hexdump | head -n 100000 | gzip > test.txt.gz
进行较小的文件测试
cat /dev/urandom | hexdump | head -n 100 | gzip > test.txt.gz
不使用-m1
选项运行(警告:将发送垃圾邮件终端)
print subprocess.check_output("zgrep 'a' test.txt.gz", shell=True)
对未压缩文件使用grep
而不是zgrep
cat /dev/urandom | hexdump | head -n 100000 > test.txt
print subprocess.check_output("grep -m1 'a' test.txt", shell=True)
在perl
中运行等效命令
perl -e 'print `zgrep -m1 'a' test.txt.gz`'
我不知道为什么python
、zgrep
、-m
选项和大文件的组合会产生这个错误。如果这些因素中的任何一个被消除,那么就没有误差。在
关于这个原因,我最好的猜测来自于阅读grep
man
页面中关于-m
选项的内容。在
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines. If the input is
standard input from a regular file, and NUM matching lines are
output, grep ensures that the standard input is positioned to
just after the last matching line before exiting, regardless of
the presence of trailing context lines. This enables a calling
process to resume a search. When grep stops after NUM matching
lines, it outputs any trailing context lines.
我最初假设-m
选项只会导致grep
在找到NUM个匹配项后退出。但是grep
和标准输入可能有点奇怪。但这仍然不能解释为什么只有大压缩文件才会出现错误。在
最后,我将我的脚本从python移植到perl来解决这个问题,所以现在不需要任何解决方案。但我真的很想更好地理解为什么这场完美的风暴会失败。在
zgrep只是一个shell脚本,它大致相当于
gunzip test.txt.gz | grep -m1 'a'
。gunzip只是提取出块并传递给grep。然后,当grep找到模式时,它就会退出。在如果gunzip到那时还没有完成文件的解压缩,将来写入gunzip的stdout(它连接到grep的stdin)将失败。这正是你的情况:
多亏了米尔斯夫,本文完美地解释了这一点: https://blog.nelhage.com/2010/02/a-very-subtle-bug/
python代码应该改为:
相关问题 更多 >
编程相关推荐