我正在使用python脚本对zgrep
进行系统调用,并仅使用-m1
选项打印第一个结果。
#! /usr/bin/env python2.7
import subprocess
print subprocess.check_output("zgrep -m1 'a' test.txt.gz", shell=True)
在大文件(+ 2MB)上运行脚本时,会生成以下错误。
> ./broken-zgrep.py
gzip: stdout: Broken pipe
Traceback (most recent call last):
File "./broken-zgrep.py", line 25, in <module>
print subprocess.check_output("zgrep -m1 'a' test.txt.gz", shell=True)
File "/usr/intel/pkgs/python/2.7/lib/python2.7/subprocess.py", line 537, in check_output
raise CalledProcessError(retcode, cmd, output=output)
subprocess.CalledProcessError: Command 'zgrep -m1 'a' test.txt.gz' returned non-zero exit status 2
但是,如果我复制python抱怨的命令并直接在shell中运行它,它可以正常工作。
> zgrep -m1 'a' test.txt.gz
0000000 8c82 524d 67a4 c37d 0595 a457 b110 3192
在shell中手动运行后,命令的退出状态为0
,表示成功。 Python说该命令以错误代码2
退出。
> echo $?
0
以下是如何制作示例测试文件以重现错误。它会创建一个100000行随机值的hex文件,并使用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以解决这个问题,因此不需要立即解决问题。但我真的希望更好地理解为什么这种完美的风暴会失败。
答案 0 :(得分:4)
zgrep只是一个shell脚本,大致相当于gunzip test.txt.gz | grep -m1 'a'
。枪弹只是提取大块并将它们传递给grep。然后,当grep找到模式时,它会退出。
如果gunzip还没有完成解压缩文件,那么将来写入gunzip的stdout(连接到grep的stdin)将会失败。这正是您的案例中发生的事情:
gzip: stdout: Broken pipe
答案 1 :(得分:2)
感谢MilesF,本文完美地解释了它: https://blog.nelhage.com/2010/02/a-very-subtle-bug/
python代码应更改为:
import subprocess
import signal
print subprocess.check_output("zgrep -m1 'a' test.txt.gz", shell=True, , preexec_fn=lambda:signal.signal(signal.SIGPIPE, signal.SIG_DFL))