多くの役立つ回答からの情報を、いくつかの追加情報とともにまとめます。
標準の Unix シグナルは、パイプからSIGPIPE
読み取るプロセスがなくなったときに、パイプに書き込むプロセスに送信されます。
- これは必ずしもエラー状態ではありません。などの一部の Unix ユーティリティは、十分なデータを受信すると、パイプからの読み取りを途中で停止するよう
head
に設計されています。
- したがって、このエラーを引き起こす簡単な方法は
head
[1]にパイプすることです。例えば:
python -c 'for x in range(10000): print(x)' | head -n 1
デフォルトでは、つまり、書き込みプロセスが明示的にトラップしない場合、書き込みプロセスは単純に終了し、その終了コードは に設定されます SIGPIPE
141
128
。これは、 (一般にシグナルによる終了を通知するため) + 13
(SIGPIPE
の特定のシグナル番号)として計算されます。 .
ただし、設計上、Python自体がそれをトラップSIGPIPE
し、値を持つ Python (Python 3) / (Python 2)インスタンスに変換BrokenPipeError
IOError
errno
しますerrno.EPIPE
。
- 注: Windowsで Unix エミュレーション環境を使用している場合、エラーの表示が異なる場合があります。この回答を参照してください。
Pythonスクリプトが例外をキャッチしない場合、 Pythonはエラー メッセージBrokenPipeError: [Errno 32] Broken pipe
( Python 3、おそらく2 回、Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
間に挟まれている) / IOError: [Errno 32] Broken pipe
( Python 2 ) を出力し、スクリプトを終了コード1
[2]で終了します- これがヨハネス (OP ) 鋸。
Windowsに関する考慮事項 (SIGPIPE
は Unix のみのシグナルです)
この問題を解決するには、次の 2 つの方法があります。
通常、この例外を黙らせることはお勧めできません。ネットワーク ソケットの受信側が予期せず閉じるなど、スクリプトの目的によっては、重大なエラー状態を示す可能性があるためです。
- ただし、スクリプトがコマンド ライン ユーティリティである場合は、静かな終了が許容されるだけでなく、標準ユーティリティとうまく連携するために好ましい
head
場合があります。たとえば、プラットフォームのデフォルト シグナル ハンドラをインストールするために次のように静かに中止できます。 (上記のように動作します)、akhanの回答にも示されているように(Python 3と2の両方で動作します):signal.signal()
# ONLY SUITABLE FOR COMMAND-LINE UTILITIES
# Install the default signal handler.
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
# Start printing many lines.
# If this gets interrupted with SIGPIPE,
# the script aborts quietly, and the process exit code is set to
# 141 (128 + SIGPIPE)
for x in range(10000): print(x)
- それ以外の場合、 SIGPIPE によってトリガーされた例外を自分で処理したい場合(Python 3 と 2 の両方で動作し、docsから適応されます):
import sys, os, errno
try:
# Start printing many lines.
for x in range(10000): print(x)
# IMPORTANT: Flush stdout here, to ensure that the
# SIGPIPE-triggered exception can be caught.
sys.stdout.flush()
except IOError as e:
# Note: Python 3 has the more specific BrokenPipeError,
# but this way the code works in Python 2 too.
if e.errno != errno.EPIPE: raise e # Unrelated error, re-throw.
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
# ... perform other handling.
# Note: You can't write to stdout here.
# (print() and sys.stdout.write won't work)
# However, sys.stderr.write() can be used.
sys.stderr.write("SIGPIPE received, terminating.\n")
# Finally, exit with an exit code of choice.
sys.exit(141)
[1]bash
デフォルトではhead
の終了コードのみが表示されることに注意してください。これは後で0
反映されます。Python の終了コードを表示するために$?
使用します。echo ${PIPESTATUS[0]}
[2] 不思議なことに、macOS 10.15.7 (Catalina) で Python 3.9.2 (ただし 2.x ではない) を使用すると、 exit code が表示されます120
が、ドキュメントには1
と記載されており、これは Linux でも見られます。