Mam bardzo prosty skrypt w Pythonie 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()
Ale zawsze mówi:
IOError: [Errno 32] Broken pipe
Widziałem w Internecie wszystkie skomplikowane sposoby rozwiązania tego problemu, ale skopiowałem ten kod bezpośrednio, więc myślę, że coś jest nie tak z kodem, a nie SIGPIPE Pythona.
Przekierowuję dane wyjściowe, więc jeśli powyższy skrypt został nazwany „open.py”, moje polecenie do uruchomienia wyglądałoby tak:
open.py | othercommand
python
python-3.x
sigpipe
JOHANNES_NYÅTT
źródło
źródło
print(f1.readlines())
a.txt
i zapisstdout
. Być może spróbuj podzielić je na oddzielne wiersze, aby zobaczyć, która operacja wyzwala wyjątek. Jeślistdout
jest to potok i koniec odczytu został zamknięty, może to wyjaśniaćEPIPE
błąd.print
jest połączenie. @ JOHANNES_NYÅTT, czy możesz wyjaśnić, w jaki sposób uruchamiasz skrypt w Pythonie? Czy przekierowujesz gdzieś standardowe wyjście?Odpowiedzi:
Nie odtworzyłem problemu, ale być może ta metoda go rozwiązała: (pisanie wiersz po wierszu
stdout
zamiast używaniaprint
)import sys with open('a.txt', 'r') as f1: for line in f1: sys.stdout.write(line)
Możesz złapać pękniętą rurę? Spowoduje to zapisanie pliku
stdout
wiersz po wierszu, aż potok zostanie zamknięty.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
Musisz także upewnić się, że
othercommand
czyta z rury, zanim zrobi się za duża - /unix/11946/how-big-is-the-pipe-bufferźródło
print
wywołaniem, a nie odczytem plików).Problem jest spowodowany obsługą SIGPIPE. Możesz rozwiązać ten problem za pomocą następującego kodu:
from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)
Zobacz tutaj, aby zapoznać się z tłem tego rozwiązania. Lepiej odpowiedz tutaj .
źródło
Przynieść użytecznej odpowiedzi Alex L. za , użytecznej odpowiedzi akhan męska i użytecznej odpowiedzi Blckknght za razem z dodatkowymi informacjami:
Standardowy sygnał Unix
SIGPIPE
jest wysyłany do procesu zapisującego do potoku, gdy nie ma już odczytu procesu z potoku (już).head
projekt, przestają przedwcześnie czytać z potoku, gdy otrzymają wystarczającą ilość danych.Domyślnie - tj. Jeśli proces zapisu nie jest jawnie przechwytywany
SIGPIPE
- proces zapisu jest po prostu kończony , a jego kod zakończenia jest ustawiony na141
, co jest obliczane jako128
(ogólnie rzecz biorąc, aby zakończyć sygnał przez sygnał) +13
(SIGPIPE
określony numer sygnału ) .Jednak z założenia sam Python przechwytuje
SIGPIPE
i tłumaczy je naIOError
instancję Pythona zerrno
wartościąerrno.EPIPE
, dzięki czemu skrypt Pythona może ją przechwycić, jeśli tak zdecyduje - zobacz odpowiedź Alexa L., jak to zrobić.Jeśli skrypt w Pythonie go nie wychwyci , Python wyświetla komunikat o błędzie
IOError: [Errno 32] Broken pipe
i kończy skrypt kodem zakończenia1
- to jest objaw, który zobaczył OP.W wielu przypadkach jest to bardziej uciążliwe niż pomocne , więc powrót do domyślnego zachowania jest pożądany :
Korzystanie z
signal
modułu pozwala właśnie na to, jak stwierdzono w odpowiedzi Achana ;signal.signal()
przyjmuje sygnał do obsłużenia jako pierwszy argument i funkcję obsługi jako drugi; specjalna wartość obsługiSIG_DFL
reprezentuje domyślne zachowanie systemu:from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
źródło
Podczas próby zapisu do potoku, który został zamknięty na drugim końcu, pojawia się błąd „Zerwana rura”. Ponieważ kod, który pokazałeś, nie obejmuje bezpośrednio żadnych potoków, podejrzewam, że robisz coś poza Pythonem, aby przekierować standardowe wyjście interpretera Pythona do innego miejsca. Może się to zdarzyć, jeśli uruchamiasz taki skrypt:
Problem polega na tym, że
someothercommand
wychodzi się bez czytania wszystkiego, co jest dostępne na jego standardowym wejściu. To powoduje, że twój zapis (przezprint
w pewnym momencie ) kończy się niepowodzeniem.Udało mi się odtworzyć błąd za pomocą następującego polecenia w systemie Linux:
python -c 'for i in range(1000): print i' | less
Jeśli zamknę
less
pager bez przewijania wszystkich jego danych wejściowych (1000 linii), Python zakończy pracę z tym samymIOError
, co zgłosiłeś.źródło
SIGPIPE
sygnał nie musi oznaczać błędu warunku; niektóre narzędzia Unix, w szczególnościhead
, przez projekt, podczas normalnej pracy blisko rura wcześnie, raz oni czytać jak najwięcej danych, jak to potrzebne.Czuję się zobowiązany zwrócić uwagę, że metoda przy użyciu
jest rzeczywiście niebezpieczny (jak już zasugerował David Bennet w komentarzach) iw moim przypadku doprowadził do zabawnego biznesu zależnego od platformy w połączeniu z
multiprocessing.Manager
(ponieważ standardowa biblioteka polega na podnoszeniu BrokenPipeError w kilku miejscach). Krótko mówiąc, długą i bolesną historię naprawiłem w następujący sposób:Najpierw musisz złapać
IOError
(Python 2) lubBrokenPipeError
(Python 3). W zależności od programu możesz w tym momencie spróbować zakończyć pracę wcześniej lub po prostu zignorować wyjątek: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
Jednak to nie wystarczy. Python 3 może nadal wyświetlać następujący komunikat:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> BrokenPipeError: [Errno 32] Broken pipe
Niestety pozbycie się tej wiadomości nie jest proste, ale w końcu znalazłem http://bugs.python.org/issue11380, gdzie Robert Collins sugeruje to obejście, które zmieniłem w dekorator, którym możesz zawinąć swoją główną funkcję (tak, to trochę szalone wcięcie):
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
źródło
Wiem, że to nie jest „właściwy” sposób, aby to zrobić, ale jeśli chcesz po prostu pozbyć się komunikatu o błędzie, możesz wypróbować to obejście:
python your_python_code.py 2> /dev/null | other_command
źródło
Najwyższa odpowiedź (
if e.errno == errno.EPIPE:
) tutaj tak naprawdę nie działa dla mnie. Mam:AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'
Powinno to jednak zadziałać, jeśli wszystko, na czym ci zależy, to ignorowanie pękniętych rur w określonych zapisach. Myślę, że to bezpieczniejsze niż złapanie SIGPIPE:
try: # writing, flushing, whatever goes here except BrokenPipeError: exit( 0 )
Oczywiście musisz podjąć decyzję, czy twój kod jest naprawdę, naprawdę zrobiony, jeśli trafisz na zepsutą potokę, ale w większości przypadków myślę, że zwykle będzie to prawda. (Nie zapomnij zamknąć uchwytów plików itp.)
źródło
Może to również wystąpić, jeśli koniec odczytu danych wyjściowych skryptu umiera przedwcześnie
czyli open.py | otherCommand
jeśli otherCommand kończy działanie i open.py próbuje pisać na standardowe wyjście
Miałem zły scenariusz gawk, który zrobił mi to cudownie.
źródło
head
, przez projekt, podczas normalnej pracy blisko rura wcześnie, raz oni czytać jak najwięcej danych, jak to potrzebne. Większość CLI po prostu odracza system ze względu na jego domyślne zachowanie: ciche zakończenie procesu odczytu i zgłoszenie kodu zakończenia141
(co w powłoce nie jest łatwo widoczne, ponieważ ostatnie polecenie potoku określa ogólny kod zakończenia). Niestety, domyślnym zachowaniem Pythona jest głośna śmierć .Zamknięcia należy wykonywać w odwrotnej kolejności do otwierania.
źródło