Errore IO: [Errno 32] Pipe interrotta: Python


88

Ho uno script Python 3 molto semplice:

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

Ma dice sempre:

IOError: [Errno 32] Broken pipe

Ho visto su Internet tutti i modi complicati per risolvere questo problema, ma ho copiato direttamente questo codice, quindi penso che ci sia qualcosa di sbagliato nel codice e non nel SIGPIPE di Python.

Sto reindirizzando l'output, quindi se lo script sopra è stato chiamato "open.py", il mio comando da eseguire sarebbe:

open.py | othercommand

@squiguy linea 2:print(f1.readlines())
JOHANNES_NYÅTT

2
Hai due operazioni di I / O che si verificano sulla riga 2: una lettura da a.txte una scrittura su stdout. Forse prova a dividerli in righe separate in modo da poter vedere quale operazione attiva l'eccezione. Se stdoutè una pipe e l'estremità di lettura è stata chiusa, ciò potrebbe spiegare l' EPIPEerrore.
James Henstridge

1
Posso riprodurre questo errore in uscita (date le giuste condizioni), quindi sospetto che la printchiamata sia il colpevole. @ JOHANNES_NYÅTT, puoi chiarire come stai avviando il tuo script Python? Stai reindirizzando l'output standard da qualche parte?
Blckknght

2
Questa è una possibile duplicato della seguente domanda: stackoverflow.com/questions/11423225/...

Risposte:


45

Non ho riprodotto il problema, ma forse questo metodo lo risolverà: (scrivendo riga per riga stdoutanziché utilizzare print)

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

Potresti prendere il tubo rotto? Questo scrive il file stdoutriga per riga fino alla chiusura della pipe.

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

Devi anche assicurarti che othercommandstia leggendo dalla pipe prima che diventi troppo grande - /unix/11946/how-big-is-the-pipe-buffer


7
Sebbene questa sia una buona pratica di programmazione, non penso che abbia nulla a che fare con l'errore di pipe rotto che l'interrogante sta ricevendo (che probabilmente ha a che fare con la printchiamata, non con la lettura dei file).
Blckknght

@Blckknght Ho aggiunto alcune domande e metodi alternativi e speravo in un feedback dall'autore. Se il problema sta inviando una grande quantità di dati da un file aperto direttamente all'istruzione print, forse una delle alternative sopra potrebbe risolverlo.
Alex L

(Le soluzioni più semplici sono spesso le migliori - a meno che non ci sia un motivo particolare per caricare un intero file e poi stamparlo, fallo in un modo diverso)
Alex L

1
Fantastico lavoro nella risoluzione dei problemi! Sebbene potessi dare questa risposta per scontata, ho potuto apprezzarla solo dopo aver visto come le altre risposte (e il mio approccio) impallidivano rispetto alla tua risposta.
Jesvin Jose

117

Il problema è dovuto alla manipolazione di SIGPIPE. Puoi risolvere questo problema utilizzando il codice seguente:

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

Vedi qui per informazioni generali su questa soluzione. Meglio rispondere qui .


13
Questo è molto pericoloso, come ho appena scoperto, perché se mai dovessi ottenere un SIGPIPE su un socket (httplib o qualsiasi altra cosa), il tuo programma uscirà senza preavviso o errore.
David Bennett

1
@DavidBennett, sono sicuro che la sua applicazione dipende e per i tuoi scopi la risposta accettata è quella giusta. C'è una domanda e risposta molto approfondita qui per le persone da esaminare e prendere una decisione informata. IMO, per gli strumenti da riga di comando, probabilmente è meglio ignorare il segnale pipe nella maggior parte dei casi.
akhan

1
Qualche modo per farlo solo temporaneamente?
Nate Glenn

2
@NateGlenn È possibile salvare il gestore esistente e ripristinarlo in un secondo momento.
akhan

3
Qualcuno potrebbe rispondermi perché le persone considerano un articolo di blogspot come una migliore fonte di verità rispetto alla documentazione ufficiale (suggerimento: apri il collegamento per vedere come correggere correttamente l'errore del tubo rotto)? :)
Yurii Rabeshko

92

Per portare la risposta utile di Alex L. , la risposta utile di akhan e la risposta utile di Blckknght insieme ad alcune informazioni aggiuntive:

  • Il segnale Unix standardSIGPIPE viene inviato a un processo che scrive su una pipe quando non ci sono (più) processi in lettura dalla pipe.

    • Questa non è necessariamente una condizione di errore ; alcune utilità Unix, come head per esempio , interrompono la lettura prematuramente da una pipe, una volta che hanno ricevuto dati sufficienti.
  • Per impostazione predefinita , ovvero se il processo di scrittura non esegue il trap esplicito SIGPIPE, il processo di scrittura viene semplicemente terminato e il suo codice di uscita è impostato su141 , che viene calcolato come 128(per segnalare la terminazione per segnale in generale) + 13( numero diSIGPIPE segnale specifico di ) .

  • In base alla progettazione, tuttavia, Python stesso lo intrappolaSIGPIPE e lo traduce inIOError un'istanza Python con errnovalore errno.EPIPE, in modo che uno script Python possa catturarlo, se lo desidera - vedere la risposta di Alex L. su come farlo.

  • Se uno script Python non lo rileva , Python restituisce un messaggio di erroreIOError: [Errno 32] Broken pipe e termina lo script con un codice di uscita1 : questo è il sintomo visto dall'OP.

  • In molti casi questo è più distruttivo che utile , quindi è auspicabile ripristinare il comportamento predefinito :

    • L'uso del signalmodulo consente proprio questo, come affermato nella risposta di akhan ; signal.signal()accetta un segnale da gestire come primo argomento e un gestore come secondo; il valore del gestore speciale SIG_DFLrappresenta il comportamento predefinito del sistema :

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

32

Quando si tenta di scrivere su una pipe che è stata chiusa all'altra estremità, si verifica un errore di "pipe rotto". Poiché il codice che hai mostrato non coinvolge direttamente alcun pipe, sospetto che tu stia facendo qualcosa al di fuori di Python per reindirizzare l'output standard dell'interprete Python da qualche altra parte. Questo potrebbe accadere se stai eseguendo uno script come questo:

python foo.py | someothercommand

Il problema che hai è che stai someothercommanduscendo senza leggere tutto ciò che è disponibile nel suo input standard. Questo fa sì che la tua scrittura (tramite print) fallisca ad un certo punto.

Sono stato in grado di riprodurre l'errore con il seguente comando su un sistema Linux:

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

Se chiudo il lesscercapersone senza scorrere tutto il suo input (1000 righe), Python esce con lo stesso IOErrorche hai segnalato.


10
Sì, questo è vero, ma come lo risolvo?
JOHANNES_NYÅTT

2
per favore fammi sapere come risolverlo.
JOHANNES_NYÅTT

1
@ JOHANNES_NYÅTT: Potrebbe funzionare per file piccoli a causa del buffering fornito dalle pipe sulla maggior parte dei sistemi Unix. Se puoi scrivere l'intero contenuto dei file nel buffer, non genera un errore se l'altro programma non legge mai quei dati. Tuttavia, se la scrittura si blocca (perché il buffer è pieno), fallirà quando si chiude l'altro programma. Per dire di nuovo: qual è l'altro comando? Non possiamo più aiutarti con solo il codice Python (poiché non è la parte che sta facendo la cosa sbagliata).
Blckknght

1
Ho avuto questo problema durante il collegamento alla testa ... eccezione dopo dieci righe di output. Abbastanza logico, ma comunque inaspettato :)
André Laszlo

4
@Blckknght: Buone informazioni in generale, ma " aggiustalo : e" la parte che sta facendo la cosa sbagliata ": un SIGPIPEsegnale non indica necessariamente una condizione di errore ; alcune utilità Unix, in particolare head, in base alla progettazione, durante il normale funzionamento chiudono il pipe presto, una volta che hanno letto tutti i dati di cui avevano bisogno.
mklement0

20

Mi sento in dovere di sottolineare che il metodo utilizzato

signal(SIGPIPE, SIG_DFL) 

è davvero pericoloso (come già suggerito da David Bennet nei commenti) e nel mio caso ha portato a divertenti affari dipendenti dalla piattaforma quando combinato con multiprocessing.Manager(perché la libreria standard si basa sul fatto che BrokenPipeError viene sollevato in diversi punti). Per far breve una storia lunga e dolorosa, ecco come l'ho risolta:

Per prima cosa, devi catturare IOError(Python 2) o BrokenPipeError(Python 3). A seconda del programma, puoi provare a uscire in anticipo a quel punto o semplicemente ignorare l'eccezione:

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

Tuttavia, questo non è sufficiente. Python 3 potrebbe ancora stampare un messaggio come questo:

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

Sfortunatamente sbarazzarsi di quel messaggio non è semplice, ma alla fine ho trovato http://bugs.python.org/issue11380 dove Robert Collins suggerisce questa soluzione alternativa che ho trasformato in un decoratore con cui puoi avvolgere la tua funzione principale (sì, è un po 'folle rientro):

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

2
Questo non sembra risolverlo per me.
Kyle Bridenstine

Ha funzionato per me dopo aver aggiunto eccetto BrokenPipeError: passare alla funzione supress_broken_pipe_msg
Rupen B

2

So che questo non è il modo "corretto" per farlo, ma se sei semplicemente interessato a sbarazzarti del messaggio di errore, potresti provare questa soluzione alternativa:

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

2

La risposta migliore ( if e.errno == errno.EPIPE:) qui non ha funzionato per me. Ho ottenuto:

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

Tuttavia, questo dovrebbe funzionare se tutto ciò che ti interessa è ignorare le pipe rotte su scritture specifiche. Penso che sia più sicuro che intrappolare SIGPIPE:

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

Ovviamente devi decidere se il tuo codice è davvero, veramente fatto se colpisci il tubo rotto, ma per la maggior parte degli scopi penso che di solito sia vero. (Non dimenticare di chiudere gli handle di file, ecc.)


1

Ciò può verificarsi anche se la fine di lettura dell'output dallo script muore prematuramente

cioè open.py | otherCommand

se otherCommand esce e open.py prova a scrivere su stdout

Avevo una sceneggiatura pessima che mi ha fatto questo adorabile.


2
Non si tratta necessariamente del processo di lettura dal pipe che muore , necessariamente: alcune utility Unix, in particolare head, in base alla progettazione, durante il normale funzionamento chiudono il pipe in anticipo, una volta che hanno letto tutti i dati di cui hanno bisogno. La maggior parte delle CLI rimanda semplicemente al sistema per il suo comportamento predefinito: terminare silenziosamente il processo di lettura e riportare il codice di uscita 141(che, in una shell, non è immediatamente evidente, perché l' ultimo comando di una pipeline determina il codice di uscita complessivo). Il comportamento predefinito di Python , sfortunatamente, è morire rumorosamente .
mklement0

-1

Le chiusure devono essere eseguite in ordine inverso rispetto alle aperture.


4
Sebbene questa sia una buona pratica in generale, non fare non è un problema in sé e non spiega i sintomi dell'OP.
mklement0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.