4

私は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 が文句を言うコマンドをコピーしてシェルで直接実行すると、問題なく動作します。

> zgrep -m1 'a' test.txt.gz
0000000 8c82 524d 67a4 c37d 0595 a457 b110 3192

コマンドの終了ステータスは0、シェルで手動で実行した後のものであり、成功を示しています。Python は、コマンドがエラー コードで終了すると言います2

> echo $?
0

エラーを再現するためのサンプル テスト ファイルの作成方法を次に示します。ランダム値の 100000 行の 16 進ファイルを作成し、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 に移植することになったので、すぐに解決策を講じる必要はありません。しかし、この完璧な状況の嵐がなぜ失敗するのかをもっとよく理解したいと思っています。

4

2 に答える 2

4

zgrep は単なるシェル スクリプトであり、ほぼgunzip test.txt.gz | grep -m1 'a'. gunzip は単にチャンクを抽出して grep に渡します。次に、grep がパターンを見つけると終了します。

それまでに gunzip がファイルの解凍を完了していない場合、今後の gunzip の stdout (grep の stdin に接続されている) への書き込みは失敗します。これはまさにあなたの場合に起こっていることです:

gzip: stdout: Broken pipe
于 2012-06-07T21:27:17.153 に答える
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))
于 2016-07-20T15:27:17.130 に答える