2013-01-08 3 views
58

ho una molto semplice Python 3 script:IOError: [Errno 32] tubo rotto: Python

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 si dice sempre:

IOError: [Errno 32] Broken pipe 

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

sto reindirizzando l'output, quindi se lo script precedente è stato nominato "open.py", quindi il mio comando per eseguire sarebbe:

open.py | othercommand 
+2

Per quale numero di riga? – squiguy

+0

@squiguy linea 2: 'print (f1.readlines())' –

+2

Hai due operazioni di I/O che si verificano sulla riga 2: una lettura da 'a.txt' e una scrittura su' stdout'. Forse prova a dividerli su linee separate in modo da poter vedere quale operazione fa scattare l'eccezione. Se 'stdout' è una pipe e la fine di lettura è stata chiusa, allora ciò potrebbe tenere conto dell'errore' EPIPE'. –

risposta

22

non ho riprodotto il problema, ma forse questo metodo avrebbe risolverlo: (scrivendo riga per riga a stdout invece di usare print)

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

si poteva prendere il tubo rotto? Questo scrive il file su stdout riga per riga fino a quando la pipe non viene chiusa.

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 

È inoltre necessario assicurarsi che othercommand sta leggendo dal tubo prima che diventi troppo grande - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

+5

Sebbene si tratti di 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 chiamata 'print', non con la lettura dei file) . – Blckknght

+0

@Blckknght Ho aggiunto alcune domande e metodi alternativi e speravo in qualche feedback dall'autore. Se il problema è l'invio di una grande quantità di dati da un file aperto direttamente all'istruzione print, allora forse una delle alternative sopra la risolverebbe. –

+0

(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, farlo in un modo diverso) –

23

un errore di "Broken Pipe" si verifica quando si tenta di scrivere ad un tubo che è stato chiuso dall'altra parte. Dato che il codice che hai mostrato non coinvolge direttamente alcun pipe, sospetto che tu stia facendo qualcosa al di fuori di Python per reindirizzare lo standard output dell'interprete Python da qualche altra parte. Questo potrebbe accadere se si sta eseguendo uno script come questo:

python foo.py | someothercommand 

Il problema che ho è che someothercommand sta uscendo senza leggere tutto a disposizione sul suo standard input. Ciò causa la scrittura (via print) in errore a un certo punto.

ho potuto riprodurre l'errore con il seguente comando su un sistema Linux:

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

se viene chiusa la less pager senza scorrere attraverso tutto il suo ingresso (1000 linee), Python esce con la stessa IOError che hai segnalato.

+5

Sì, questo è vero, ma come lo risolvo? –

+0

@ JOHANNES_NYÅTT: Probabilmente non è qualcosa che puoi risolvere dal tuo programma Python. Invece, è necessario scoprire perché l'altro programma sta uscendo e correggerlo (cambiando il suo codice, se è possibile, o chiamandolo in modo diverso). Potremmo essere in grado di aiutarti, ma dovrai dirci cosa sia 'othercommand' e cosa ti aspetti che faccia con l'input che stai inviando. – Blckknght

+1

per favore fammi sapere come risolverlo. –

95

Il problema è dovuto alla gestione SIGPIPE. È possibile risolvere questo problema utilizzando il seguente codice:

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

See here per lo sfondo su questa soluzione.

+1

Grazie a @akhan, la soluzione risolveva il mio problema mentre gli altri non lo facevano, stavo eseguendo il piping dell'output di stampa del mio programma python in (. /my_program.py | head). – Morlock

+3

Non so perché funzioni, ma è il mio eroe –

+0

Posso confermare: lavora –

1

Questo può verificarsi anche se la fine di leggere dell'uscita dal vostro script muore prematuramente

cioè open.py | otherCommand

se uscite otherCommand e open.py cerca di scrivere a stdout

ho avuto uno script gawk male che ha fatto questa bella per me.

+0

Non si tratta della lettura del processo dal pipe _dying_, necessariamente: alcune utility Unix, in particolare 'head', _by design, durante il normale operation_ chiudono il pipe in anticipo, una volta che hanno letto quanti più dati necessari. La maggior parte delle CLI semplicemente rimandano al sistema per il suo comportamento predefinito: interrompendo silenziosamente il processo di lettura e riportando il codice di uscita '141' (che, in una shell, non è immediatamente evidente, perché il comando _last_ di una pipeline determina il codice di uscita generale). Il comportamento predefinito di _Python_, sfortunatamente, è quello di morire _noisily_. – mklement0

0

Chiusura deve essere eseguita nell'ordine inverso all'apertura.

+0

Mentre questa è una buona pratica in generale, non fare non è un problema in sé e non spiega i sintomi dell'OP. – mklement0

54

Per portare Alex L.'s helpful answer, akhan's helpful answer, e Blckknght's helpful answer insieme ad alcune informazioni aggiuntive:

  • Standard Unix signal SIGPIPE viene inviato ad un processo scrittura ad un pipe quando non c'è alcun processo lettura dal tubo (più).

    • Questo non è necessariamente un errore condizione; alcune utilità Unix come headdi progettazione interrompono la lettura prematuramente da una pipe, una volta che hanno ricevuto abbastanza dati.
  • Per default - cioè se il processo scrittura non esplicitamente trappolaSIGPIPE - il processo di scrittura è semplicemente terminato, e il suo codice di uscita è impostata 141, che viene calcolato come 128 (per segnalare la terminazione tramite segnale in generale) + 13 (, il segnale specifico numero).

  • In base alla progettazione, tuttavia, Python stesso trappole SIGPIPE e traduce in un Python IOError un'istanza con errno valore errno.EPIPE, in modo che uno script Python può prenderlo, se lo desidera - vedere Alex L.'s answer per come farlo.

  • Se un Python sceneggiatura fa non cattura è, Python uscite messaggio di errore IOError: [Errno 32] Broken pipe e termina lo script con codice di uscita 1 - questo è il sintomo la sega OP.

  • In molti casi questo è più dirompente che utile, così Ripristino del comportamento predefinito è auspicabile:

    • Utilizzando il modulosignal permette proprio questo, come indicato in akhan's answer; signal.signal() accetta un segnale da gestire come primo argomento e un gestore come secondo; valore gestore speciale SIG_DFL rappresenta predefinita il comportamento del sistema:

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

Questa è una grande spiegazione. – ltw

14

mi sento in dovere di sottolineare che il metodo che utilizza

signal(SIGPIPE, SIG_DFL) 

è davvero pericoloso (come già suggerito da David Bennet nei commenti) e nel mio caso ha portato a affari divertenti dipendenti dalla piattaforma quando pettine integrato con multiprocessing.Manager (poiché la libreria standard si basa su BrokenPipeError che viene generato in più punti). Per farla breve e dolorosa, ecco come ho risolto:

Per prima cosa, è necessario prendere lo IOError (Python 2) o BrokenPipeError (Python 3). A seconda del programma che si può provare a uscire presto 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 possono ancora stampare un messaggio come questo:

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

Purtroppo sbarazzarsi di quel messaggio non è semplice, ma alla fine ho trovato http://bugs.python.org/issue11380 dove Robert Collins suggerisce questa soluzione che ho trasformato in un decoratore si può avvolgere la funzione principale con (sì, questo è un indovinello folle):

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