IOError: [Errno 32] Pipe defekt: Python

85

Ich habe ein sehr einfaches Python 3-Skript:

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()

Aber es heißt immer:

IOError: [Errno 32] Broken pipe

Ich habe im Internet alle komplizierten Möglichkeiten gesehen, dies zu beheben, aber ich habe diesen Code direkt kopiert, sodass ich denke, dass etwas mit dem Code nicht stimmt und nicht mit Pythons SIGPIPE.

Ich leite die Ausgabe um. Wenn das obige Skript "open.py" heißt, lautet mein Befehl zum Ausführen:

open.py | othercommand
JOHANNES_NYÅTT
quelle
@squiguy Zeile 2:print(f1.readlines())
JOHANNES_NYÅTT
2
In Zeile 2 treten zwei E / A-Vorgänge auf: ein Lesen von a.txtund ein Schreiben von stdout. Versuchen Sie möglicherweise, diese in separate Zeilen aufzuteilen, damit Sie sehen können, welche Operation die Ausnahme auslöst. Wenn stdoutes sich um eine Pipe handelt und das Leseende geschlossen wurde, kann dies den EPIPEFehler erklären .
James Henstridge
1
Ich kann diesen Fehler bei der Ausgabe reproduzieren (unter den richtigen Bedingungen), daher vermute ich, dass der printAnruf der Schuldige ist. @ JOHANNES_NYÅTT, können Sie klarstellen, wie Sie Ihr Python-Skript starten? Leiten Sie die Standardausgabe irgendwo um?
Blckknght
2
Dies ist ein mögliches Duplikat der folgenden Frage: stackoverflow.com/questions/11423225/…

Antworten:

43

Ich habe das Problem nicht reproduziert, aber vielleicht würde diese Methode es lösen: (Zeile für Zeile schreiben, stdoutanstatt zu verwenden print)

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

Sie könnten das kaputte Rohr fangen? Dadurch wird die Datei stdoutzeilenweise geschrieben, bis die Pipe geschlossen wird.

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

Sie müssen auch sicherstellen, dass othercommanddie Pipe aus der Pipe gelesen wird, bevor sie zu groß wird - /unix/11946/how-big-is-the-pipe-buffer

Alex L.
quelle
7
Dies ist zwar eine gute Programmierpraxis, aber ich glaube nicht, dass dies etwas mit dem Fehler zu tun hat, den der Fragesteller erhält (was wahrscheinlich mit dem printAufruf zu tun hat , nicht mit dem Lesen der Dateien).
Blckknght
@Blckknght Ich habe ein paar Fragen und alternative Methoden hinzugefügt und hoffte auf ein Feedback vom Autor. Wenn das Problem darin besteht, eine große Datenmenge aus einer geöffneten Datei direkt an die print-Anweisung zu senden, kann dies möglicherweise durch eine der oben genannten Alternativen behoben werden.
Alex L
(Die einfachsten Lösungen sind oft die besten - es sei denn, es gibt einen bestimmten Grund, eine ganze Datei zu laden und dann zu drucken, machen Sie es anders)
Alex L
1
Tolle Arbeit bei der Fehlerbehebung! Obwohl ich diese Antwort als selbstverständlich ansehen konnte, konnte ich dies erst schätzen, nachdem ich gesehen hatte, wie die anderen Antworten (und mein eigener Ansatz) im Vergleich zu Ihrer Antwort verblasst sind.
Jesvin Jose
115

Das Problem ist auf die SIGPIPE-Behandlung zurückzuführen. Sie können dieses Problem mit dem folgenden Code lösen:

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

Hier finden Sie Hintergrundinformationen zu dieser Lösung. Besser hier antworten .

Akhan
quelle
13
Dies ist sehr gefährlich, wie ich gerade herausgefunden habe, denn wenn Sie jemals ein SIGPIPE an einem Socket (httplib oder was auch immer) erhalten, wird Ihr Programm einfach ohne Warnung oder Fehler beendet.
David Bennett
1
@ DavidBennett, ich bin sicher, dass seine Anwendung abhängig ist und für Ihre Zwecke die akzeptierte Antwort die richtige ist. Hier gibt es eine Menge gründlicher Fragen und Antworten , damit die Leute eine fundierte Entscheidung treffen können. IMO, für Befehlszeilen-Tools ist es in den meisten Fällen wahrscheinlich am besten, das Pipe-Signal zu ignorieren.
Akhan
1
Wie kann man das nur vorübergehend machen?
Nate Glenn
2
@NateGlenn Sie können den vorhandenen Handler speichern und später wiederherstellen.
Akhan
2
Könnte mir jemand antworten, warum Leute einen Blogspot-Artikel als eine bessere Quelle der Wahrheit betrachten als die offizielle Dokumentation (Hinweis: Öffnen Sie den Link, um zu sehen, wie der Fehler bei defekten Rohren richtig behoben werden kann)? :)
Yurii Rabeshko
88

Zu bringen , Alex L. hilfreiche Antwort , Akhan der hilfreiche Antwort und Blckknght des hilfreiche Antwort zusammen mit einigen zusätzlichen Informationen:

  • Das Standard-Unix-SignalSIGPIPE wird an einen Prozess gesendet, der in eine Pipe schreibt , wenn (nicht mehr) kein Prozess von der Pipe gelesen wird .

    • Dies ist nicht unbedingt eine Fehlerbedingung . Einige Unix-Dienstprogramme, z. B. head von Natur aus , hören auf, vorzeitig aus einer Pipe zu lesen, sobald sie genügend Daten erhalten haben.
  • In der Standardeinstellung - das heißt, wenn der Schreibvorgang nicht explizit Fall SIGPIPE - der Schreibvorgang wird einfach beendet , und ihr Exit - Code ist auf141 , die wie folgt berechnet wird 128(um Signal Beendigung durch das Signal im Allgemeinen) + 13( SIGPIPE‚s spezifischen Signalnummer ) .

  • Durch Design, aber Python selbst einfängtSIGPIPE und übersetzt sie in eine PythonIOError - Instanz mit errnoWert errno.EPIPE, so dass ein Python - Skript , um es zu fangen, wenn es so wählt - siehe Alex L. Antwort , wie das zu tun.

  • Wenn ein Python- Skript es nicht abfängt , gibtIOError: [Errno 32] Broken pipe Python eine Fehlermeldung aus und beendet das Skript mit dem Exit-Code1 - dies ist das Symptom, das das OP gesehen hat.

  • In vielen Fällen ist dies eher störend als hilfreich . Daher ist es wünschenswert, zum Standardverhalten zurückzukehren :

    • Die Verwendung des signalModuls ermöglicht genau das, wie in Akhans Antwort angegeben . signal.signal()nimmt ein Signal als erstes Argument und einen Handler als zweites; Der spezielle Handlerwert SIG_DFLrepräsentiert das Standardverhalten des Systems :

      from signal import signal, SIGPIPE, SIG_DFL
      signal(SIGPIPE, SIG_DFL) 
mklement0
quelle
32

Ein "Broken Pipe" -Fehler tritt auf, wenn Sie versuchen, in eine Pipe zu schreiben, die am anderen Ende geschlossen wurde. Da der von Ihnen gezeigte Code keine Pipes direkt enthält, vermute ich, dass Sie etwas außerhalb von Python tun, um die Standardausgabe des Python-Interpreters an eine andere Stelle umzuleiten. Dies kann passieren, wenn Sie ein Skript wie das folgende ausführen:

python foo.py | someothercommand

Das Problem, das Sie haben, someothercommandist das Beenden, ohne alles zu lesen, was auf der Standardeingabe verfügbar ist. Dies führt dazu, dass Ihr Schreibvorgang (via print) irgendwann fehlschlägt.

Ich konnte den Fehler mit dem folgenden Befehl auf einem Linux-System reproduzieren:

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

Wenn ich den lessPager schließe, ohne durch alle Eingaben (1000 Zeilen) zu scrollen, wird Python mit dem von IOErrorIhnen gemeldeten beendet.

Blckknght
quelle
10
Ja, das stimmt, aber wie kann ich das beheben?
JOHANNES_NYÅTT
2
Bitte lassen Sie mich wissen, wie das Problem behoben werden kann.
JOHANNES_NYÅTT
1
@ JOHANNES_NYÅTT: Aufgrund der Pufferung durch Pipes auf den meisten Unix-ähnlichen Systemen funktioniert dies möglicherweise für kleine Dateien. Wenn Sie den gesamten Inhalt der Dateien in den Puffer schreiben können, wird kein Fehler ausgegeben, wenn das andere Programm diese Daten nie liest. Wenn das Schreiben jedoch blockiert (weil der Puffer voll ist), schlägt es fehl, wenn das andere Programm beendet wird. Um es noch einmal zu sagen: Was ist der andere Befehl? Wir können Ihnen nicht mehr nur mit dem Python-Code helfen (da es nicht der Teil ist, der das Falsche tut).
Blckknght
1
Ich habe dieses Problem beim Piping to Head ... Ausnahme nach zehn Ausgabezeilen. Ganz logisch, aber immer noch unerwartet :)
André Laszlo
4
@Blckknght: Gute Informationen im Allgemeinen, aber wieder " fix , dass: und‚der Teil, der das tut , falsch Ding‘: ein SIGPIPESignal zeigt nicht notwendigerweise eine Fehlerbedingung, einige Unix - Werkzeuge, vor allem head, durch Design, während der normalen Betriebes der Nähe des Rohr früh, sobald sie so viele Daten gelesen haben, wie sie brauchten.
mklement0
20

Ich fühle mich verpflichtet darauf hinzuweisen, dass die Methode verwendet

signal(SIGPIPE, SIG_DFL) 

ist in der Tat gefährlich (wie bereits von David Bennet in den Kommentaren vorgeschlagen) und führte in meinem Fall in Kombination mit plattformabhängigen lustigen Geschäften multiprocessing.Manager(da die Standardbibliothek darauf beruht, dass BrokenPipeError an mehreren Stellen ausgelöst wird). Um es kurz und schmerzhaft zu machen: So habe ich es behoben:

Zuerst müssen Sie IOError(Python 2) oder BrokenPipeError(Python 3) fangen . Abhängig von Ihrem Programm können Sie versuchen, an diesem Punkt vorzeitig zu beenden oder die Ausnahme einfach zu ignorieren:

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

Dies ist jedoch nicht genug. Python 3 druckt möglicherweise immer noch eine Nachricht wie folgt:

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

Leider ist es nicht einfach, diese Nachricht loszuwerden, aber ich habe schließlich http://bugs.python.org/issue11380 gefunden, wo Robert Collins diese Problemumgehung vorschlägt, mit der ich mich in einen Dekorateur verwandelt habe, mit dem Sie Ihre Hauptfunktion abschließen können (ja, das ist verrückt Vertiefung):

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
trehn
quelle
2
Dies schien es für mich nicht zu beheben.
Kyle Bridenstine
Es funktionierte für mich, nachdem ich außer BrokenPipeError hinzugefügt hatte: Übergeben Sie die Funktion supress_broken_pipe_msg
Rupen B
2

Ich weiß, dass dies nicht der "richtige" Weg ist, aber wenn Sie einfach daran interessiert sind, die Fehlermeldung zu beseitigen, können Sie diese Problemumgehung versuchen:

python your_python_code.py 2> /dev/null | other_command
ssanch
quelle
1

Dies kann auch auftreten, wenn das Leseende der Ausgabe Ihres Skripts vorzeitig beendet wird

dh open.py | otherCommand

Wenn otherCommand beendet wird und open.py versucht, in stdout zu schreiben

Ich hatte ein schlechtes Gawk-Skript, das mir das sehr angetan hat.

lkreinitz
quelle
2
Es geht nicht um das Verfahren aus dem Rohr zu lesen sterben , unbedingt: einige Unix - Werkzeuge, vor allem head, durch Design, während der normalen Betriebes der Nähe des Rohr früh, nachdem sie so viele Daten gelesen hat , wie sie benötigt werden . Die meisten CLIs schieben das System einfach auf sein Standardverhalten zurück: Sie beenden den Lesevorgang leise und melden den Exit-Code 141(was in einer Shell nicht ohne weiteres ersichtlich ist, da der letzte Befehl einer Pipeline den gesamten Exit-Code bestimmt). Pythons Standardverhalten ist leider, laut zu sterben .
mklement0
1

Die Top-Antwort ( if e.errno == errno.EPIPE:) hier hat bei mir nicht wirklich funktioniert. Ich habe:

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

Dies sollte jedoch funktionieren, wenn Sie nur defekte Pipes bei bestimmten Schreibvorgängen ignorieren. Ich denke, es ist sicherer als SIGPIPE zu fangen:

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

Sie müssen natürlich eine Entscheidung treffen, ob Ihr Code wirklich, wirklich fertig ist, wenn Sie auf die kaputte Pipe treffen, aber für die meisten Zwecke denke ich, dass dies normalerweise der Fall sein wird. (Vergessen Sie nicht, Dateihandles usw. zu schließen.)

JD Baldwin
quelle
-1

Das Schließen sollte in umgekehrter Reihenfolge wie das Öffnen erfolgen.

Paul
quelle
4
Während dies im Allgemeinen eine gute Praxis ist, ist das Nicht-Tun an sich kein Problem und erklärt nicht die Symptome des OP.
mklement0