source

IOError : [Errno 32] 배관 시 배관 파손 : 'prog.py | othercmd'

nicesource 2023. 5. 9. 22:49
반응형

IOError : [Errno 32] 배관 시 배관 파손 : 'prog.py | othercmd'

나는 매우 간단한 Python 3 스크립트를 가지고 있습니다.

f1 = open('a.txt', 'r')
print(f1.readlines())
f2 = open('b.txt', 'r')
print(f2.readlines())
f3 = open('c.txt', 'r')
print(f3.readlines())
f4 = open('d.txt', 'r')
print(f4.readlines())
f1.close()
f2.close()
f3.close()
f4.close()

하지만 항상 다음과 같이 말합니다.

IOError: [Errno 32] Broken pipe

인터넷에서 이 문제를 해결하는 복잡한 방법들을 다 봤지만, 이 코드를 직접 복사해서 파이썬의 SIGPIPE가 아닌 코드에 문제가 있는 것 같습니다.

출력을 리디렉션하고 있으므로 위 스크립트의 이름이 "open.py "이면 실행 명령은 다음과 같습니다.

open.py | othercommand

이 문제는 SIGPIPE 처리로 인한 것입니다.다음 코드를 사용하여 이 문제를 해결할 수 있습니다.

from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL) 

업데이트: 댓글에서 지적한 처럼, python docs는 이미 좋은 답변을 가지고 있습니다.

솔루션에 대한 배경 정보는 여기를 참조하십시오.여기서 대답하는 게 낫겠어요.

여러 유용한 답변의 정보와 추가 정보를 함께 가져오려면 다음과 같이 하십시오.

  • 파이프에서 프로세스 읽기가 없을 때(더 이상) 파이프에 쓰는 프로세스표준 Unix 신호가 전송됩니다.

    • 이는 반드시 오류 조건이 아닙니다. 다음과 같은 일부 유닉스 유틸리티head 충분한 데이터를 수신한 후 파이프에서 너무 일찍 읽는 것을 의도적으로 중단합니다.
    • 따라서 이 오류를 유발하는 쉬운 방법은 다음과 같습니다.head[1]예:
      • python -c 'for x in range(10000): print(x)' | head -n 1
  • 기본적으로 - 즉, 쓰기 프로세스명시적으로 트랩되지 않는 경우 - 쓰기 프로세스단순히 종료되고 종료 코드는 로 설정되며, 이는 다음과 같이 계산됩니다.128 + (신호 전달) +13(SIGPIPE의 특정 신호 번호).

  • 그러나 설계상 Python 자체 Python (Python 3) / (Python 2) 인스턴스를 트랩하고 변환합니다.errnoerrno.EPIPE.

    • 참고: Windows에서 Unix 에뮬레이션 환경을 사용하는 경우 오류가 다르게 나타날 수 있습니다. 이 답변을 참조하십시오.
  • Python 스크립트가 예외를 감지하지 못하면 Python오류 메시지(Python 3, 가능하면 두 번)를 출력합니다.Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>사이에 끼어 있음) / (Python 2) 종료 코드로 스크립트를 종료합니다. 이것은 요하네스(OP)가 본 증상입니다.

Windows 고려 사항(SIGPIPEUnix 전용 신호임)

  • 스크에실하경, 을 참조하는 코드를 조건부로 해야 할 수도 있습니다.SIGPIPE 답에 나타난 바와 같이

  • 스크립트가 윈도우즈의 유닉스 하위 시스템에서 실행되는 경우SIGPIPE신호는 UNIX와 다르게 표면화될 수 있습니다. 이 답변을 참조하십시오.


이 문제를 해결하는 두 가지 방법이 있습니다.

일반적으로 예외는 스크립트의 목적에 따라 네트워크 소켓의 수신 끝이 예기치 않게 닫히는 등 심각한 오류 상태를 나타낼 수 있으므로 침묵시키는 좋습니다.

  • 그러나 스크립트명령줄 유틸리티 경우, 조용한 종료가 허용될 만 아니라 표준을 잘 실행할 수 있도록 선호됩니다.head예를 들어, 유틸리티는 칸의 답변(파이썬 3과 2 모두에서 작동함)에 표시된 것처럼 플랫폼의 기본 신호 처리기(위에서 설명한 대로 동작함)를 설치하는 데 사용하여 다음과 같이 조용히 중단할 수 있습니다.
# 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 모두에서 작동).
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)

로 참에서는 다음을 하십시오.bash됩니다.head인 의종 코드료즉 - 즉0에영된에 .$?사후에사용하다echo ${PIPESTATUS[0]}Python의 종료 코드를 확인합니다.

3.x는 가 설치된 10에서는 코드인 흥미도, 파이 3.9.2(2.x는)를이 됩니다.120하지만 서류상으로는1그리고 그것이 제가 리눅스에서도 볼 수 있는 것입니다.

문제를 재현하지는 , 이 할 수 있을 것입니다: (한 제를재지는않만지았, 이해것니다입한수있을씁다: (씩줄다음에니문로할결으현방하법아마)▁(씁▁i▁line)stdout 하는 것보다.print)

import sys
with open('a.txt', 'r') as f1:
    for line in f1:
        sys.stdout.write(line)

부러진 파이프를 잡을 수 있었습니까?이렇게 하면 파일이 기록됩니다.stdout파이프가 닫힐 때까지 한 줄씩.

import sys, errno
try:
    with open('a.txt', 'r') as f1:
        for line in f1:
            sys.stdout.write(line)
except IOError as e:
    if e.errno == errno.EPIPE:
        # Handle error

당신은 또한 그것을 확인할 필요합니다.othercommand너무 커지기 전에 파이프에서 읽는 것입니다 - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

다른 쪽 끝이 닫힌 파이프에 쓰려고 하면 "파손된 파이프" 오류가 발생합니다.당신이 보여준 코드는 파이프와 직접 관련이 없기 때문에, 저는 당신이 파이썬 인터프리터의 표준 출력을 다른 곳으로 리디렉션하기 위해 파이썬 외부에서 무언가를 하고 있다고 생각합니다.다음과 같은 스크립트를 실행하는 경우 이 문제가 발생할 수 있습니다.

python foo.py | someothercommand

당신이 가지고 있는 문제는someothercommand표준 입력에서 사용 가능한 모든 항목을 읽지 않고 종료됩니다.이로 인해 쓰기가 발생합니다(다음을 통해)print이(가) 실패합니다.

Linux 시스템에서 다음 명령을 사용하여 오류를 재현할 수 있었습니다.

python -c 'for i in range(1000): print i' | less

가 ,less모든 입력(1000줄)을 스크롤하지 않고 호출기를 사용하면 Python은 동일한 내용으로 종료됩니다.IOError보고했습니다.

나는 다음과 같은 방법을 사용한다는 것을 지적할 의무가 있다고 생각합니다.

signal(SIGPIPE, SIG_DFL) 

(댓글에서 이미 데이비드 베넷이 제안한 것처럼) 정말 위험하며, 제 경우와 결합하면 플랫폼에 의존하는 재미있는 비즈니스로 이어졌습니다.multiprocessing.Manager(표준 라이브러리는 여러 위치에서 발생하는 BrokenPipeError에 의존하기 때문에).길고 고통스러운 이야기를 요약하자면, 제가 고친 방법은 다음과 같습니다.

먼저, 당신은 그것을 잡을 필요가 있습니다.IOError 2) (피톤 2) 또는BrokenPipeError(파이썬 3).프로그램에 따라 해당 시점에서 조기 종료를 시도하거나 예외를 무시할 수 있습니다.

from errno import EPIPE

try:
    broken_pipe_exception = BrokenPipeError
except NameError:  # Python 2
    broken_pipe_exception = IOError

try:
    YOUR CODE GOES HERE
except broken_pipe_exception as exc:
    if broken_pipe_exception == IOError:
        if exc.errno != EPIPE:
            raise

하지만, 이것으로는 충분하지 않습니다.Python 3은 여전히 다음과 같은 메시지를 인쇄할 수 있습니다.

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

불행하게도 그 메시지를 없애는 것은 간단하지 않지만, 저는 마침내 로버트 콜린스가 제가 주요 기능을 포장할 수 있는 장식가로 변신한 이 해결책을 제안하는 http://bugs.python.org/issue11380 을 찾았습니다(네, 그건 미친 들여쓰기입니다).

from functools import wraps
from sys import exit, stderr, stdout
from traceback import print_exc


def suppress_broken_pipe_msg(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except SystemExit:
            raise
        except:
            print_exc()
            exit(1)
        finally:
            try:
                stdout.flush()
            finally:
                try:
                    stdout.close()
                finally:
                    try:
                        stderr.flush()
                    finally:
                        stderr.close()
    return wrapper


@suppress_broken_pipe_msg
def main():
    YOUR CODE GOES HERE

이 방법이 "적절한" 방법은 아니지만 오류 메시지를 제거하는 데 관심이 있다면 다음 해결 방법을 시도할 수 있습니다.

python your_python_code.py 2> /dev/null | other_command

은 (1번 답은)if e.errno == errno.EPIPE:여기는 제게 별로 효과가 없었습니다.내가 받은 것:

AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'

그러나 특정 쓰기에서 손상된 파이프를 무시하는 것에만 관심이 있다면 이 방법이 효과적일 것입니다.SIGPIPE를 포획하는 것보다 안전하다고 생각합니다.

try:
    # writing, flushing, whatever goes here
except BrokenPipeError:
    exit( 0 )

당신은 분명히 당신의 코드가 정말로, 정말로 당신이 깨진 파이프에 부딪혔는지에 대해 결정을 내려야 하지만, 대부분의 목적에서 그것은 보통 사실일 것이라고 생각합니다. (파일 핸들을 닫는 것 등을 잊지 마십시오.)

변수를 될 수 .PYTHONUNBUFFERED=1그러면 stdout 및 stderr 스트림이 버퍼링되지 않습니다.참조: https://docs.python.org/3/using/cmdline.html#cmdoption-u

그럼, 당신의 명령은

open.py | othercommand

다음이 됩니다.

PYTHONUNBUFFERED=1 open.py | othercommand

예:

$ python3 -m http.server | tee -a access.log
^CException ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

$ PYTHONUNBUFFERED=1 python3 -m http.server | tee -a access.log
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
^C
$ 

스크립트 출력의 읽기 끝이 너무 일찍 죽는 경우에도 이 문제가 발생할 수 있습니다.

ie open.py | other Command

다른 Command가 종료되고 open.py 가 stdout에 쓰려고 하는 경우

제게 이런 사랑스러운 행동을 한 나쁜 개코 대본이 있었습니다.

닫힘은 열린 순서의 역순으로 수행해야 합니다.

언급URL : https://stackoverflow.com/questions/14207708/ioerror-errno-32-broken-pipe-when-piping-prog-py-othercmd

반응형