8

Python でシェル スクリプトを呼び出そうとしていますが、壊れたパイプ エラーが報告され続けます (結果は問題ありませんが、STDERR でエラー メッセージを表示したくありません)。原因を特定しました。次のスニペットとして再現できます。

subprocess.call('cat /dev/zero | head -c 10 | base64', shell=True)

あああああああああああ==

猫: 書き込みエラー: 壊れたパイプ

/dev/zeroは無限ストリームですが、head -c 10そこから 10 バイトだけを読み取って終了すると、ピアがパイプを閉じたため、cat は SIGPIPE を取得します。シェルでコマンドを実行しても、壊れたパイプのエラー メッセージは表示されませんが、なぜ python で表示されるのでしょうか?

4

2 に答える 2

9

SIGPIPE シグナルに対するデフォルトのアクションは、プログラムを終了することです。Python インタープリターはこれを SIG_IGN に変更して、壊れたパイプ エラーを例外の形式でプログラムに報告できるようにします。

cat ... |head ...シェルで実行すると、catデフォルトの SIGPIPE ハンドラがあり、OS カーネルは SIGPIPE で終了します。

SIGPIPE ハンドラーをその親 (Python インタープリター) から派生させて実行catするsubprocessと、SIGPIPE は単に無視され、変数をcatチェックしてエラー メッセージを出力することでエラー自体を処理します。errno

エラー メッセージを回避するには、subprocess.call に引数をcat使用できます。preexec_fn

from signal import signal, SIGPIPE, SIG_DFL
subprocess.call(
    'cat /dev/zero | head -c 10 | base64',
    shell = True,
    preexec_fn = lambda: signal(SIGPIPE, SIG_DFL)
)
于 2012-09-16T21:58:33.810 に答える
2

この些細なケースでは、少なくともシェル コマンドを使用しても何も得られず、移植性と速度が失われます。

Python 2 コード:

>>> import base64
>>> base64.b64encode(open('/dev/zero', 'rb').read(10))
'AAAAAAAAAAAAAA=='
>>> base64.b64encode('\0' * 10)
'AAAAAAAAAAAAAA=='

strPython 3 の場合 (コードは 2.6 以降でも実行されますが、インスタンスではなく返されbytesます):

>>> import base64
>>> base64.b64encode(open('/dev/zero', 'rb').read(10))
b'AAAAAAAAAAAAAA=='
>>> base64.b64encode(b'\0' * 10)
b'AAAAAAAAAAAAAA=='

いずれの場合も、最初の例は/dev/zero(それ自体は移植性がありませんが、気にしないでください) の使用法を保持しています。

于 2012-05-07T10:30:02.650 に答える