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

2024-09-28 22:26:12 发布

您现在位置:Python中文网/ 问答频道 /正文

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


我不知道为什么pythonzgrep-m选项和大文件的组合会产生这个错误。如果这些因素中的任何一个被消除,那么就没有误差。在

关于这个原因,我最好的猜测来自于阅读grepman页面中关于-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来解决这个问题,所以现在不需要任何解决方案。但我真的很想更好地理解为什么这场完美的风暴会失败。在


Tags: 文件testtxt脚本选项错误shellgrep
2条回答

zgrep只是一个shell脚本,它大致相当于gunzip test.txt.gz | grep -m1 'a'。gunzip只是提取出块并传递给grep。然后,当grep找到模式时,它就会退出。在

如果gunzip到那时还没有完成文件的解压缩,将来写入gunzip的stdout(它连接到grep的stdin)将失败。这正是你的情况:

gzip: stdout: Broken pipe

多亏了米尔斯夫,本文完美地解释了这一点: 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))

相关问题 更多 >