zgrep系统仅从大型文件调用python后出错

时间:2012-06-07 20:57:34

标签: python grep subprocess

我正在使用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`'


我不知道为什么pythonzgrep-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以解决这个问题,因此不需要立即解决问题。但我真的希望更好地理解为什么这种完美的风暴会失败。

2 个答案:

答案 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))