IOError: Canalización rota: Python

Tengo un script de Python 3 muy simple:

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

Pero siempre dice:

 IOError: [Errno 32] Broken pipe 

Vi en Internet todas las formas complicadas de solucionar esto, pero copié este código directamente, así que creo que hay algo mal con el código y no con el SIGPIPE de Python.

Estoy redirigiendo la salida, por lo que si la secuencia de comandos anterior se llamara “open.py”, mi comando para ejecutar sería:

 open.py | othercommand 

No he reproducido el problema, pero tal vez este método lo solucionaría: (escribir línea por línea en stdout lugar de usar print )

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

¿Podrías coger el tubo roto? Esto escribe el archivo en stdout línea por línea hasta que se cierra la tubería.

 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 

También debe asegurarse de que el otro othercommand esté leyendo desde la tubería antes de que sea demasiado grande – https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

El problema se debe a la manipulación SIGPIPE. Puedes resolver este problema usando el siguiente código:

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

Consulte aquí para obtener información sobre esta solución. Mejor respuesta aquí .

Para traer la respuesta útil de Alex L. , la respuesta útil de akhan y la respuesta útil de Blckknght junto con información adicional:

  • La señal estándar de Unix SIGPIPE se envía a un proceso que se escribe en una tubería cuando ya no hay proceso de lectura de la tubería (más).

    • Esto no es necesariamente una condición de error ; Algunas utilidades de Unix, como head by design, dejan de leer prematuramente desde una tubería, una vez que han recibido suficientes datos.
  • Por defecto , es decir, si el proceso de escritura no atrapa explícitamente SIGPIPE , el proceso de escritura simplemente finaliza y su código de salida se establece en 141 , que se calcula como 128 (para indicar la terminación por señal en general) + 13 ( SIGPIPE ‘ s número de señal específica).

  • Sin embargo, por su diseño, Python atrapa SIGPIPE y lo traduce a una instancia de Python IOError con un valor errno errno.EPIPE , para que un script de Python pueda capturarlo, si así lo desea, vea la respuesta de Alex L. para saber cómo hacerlo. .

  • Si un script de Python no lo atrapa , Python IOError: [Errno 32] Broken pipe mensaje de error IOError: [Errno 32] Broken pipe y termina el script con el código de salida 1 : este es el síntoma que vio el OP.

  • En muchos casos, esto es más disruptivo que útil , por lo que es deseable volver al comportamiento predeterminado :

    • El uso del módulo de signal permite eso, como se indica en la respuesta de akhan ; signal.signal() toma una señal para manejar como el primer argumento y un manejador como el segundo; el valor del controlador especial SIG_DFL representa el comportamiento predeterminado del sistema:

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

Se produce un error de “Tubería rota” cuando intenta escribir en una tubería que se ha cerrado en el otro extremo. Dado que el código que has mostrado no involucra ninguna canalización directamente, sospecho que estás haciendo algo fuera de Python para redirigir la salida estándar del intérprete de Python a otra parte. Esto podría suceder si estás ejecutando un script como este:

 python foo.py | someothercommand 

El problema que tiene es que someothercommand está saliendo sin leer todo lo disponible en su entrada estándar. Esto hace que su escritura (a través de la print ) falle en algún momento.

Pude reproducir el error con el siguiente comando en un sistema Linux:

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

Si cierro el paginador less sin desplazarme por todas sus entradas (1000 líneas), Python sale con el mismo IOError que ha informado.

Me siento obligado a señalar que el método utilizando

 signal(SIGPIPE, SIG_DFL) 

es de hecho peligroso (como ya lo sugirió David Bennet en los comentarios) y en mi caso llevó a negocios divertidos que dependen de la plataforma cuando se combina con multiprocessing.Manager Administrador (porque la biblioteca estándar se basa en BrokenPipeError que se está creando en varios lugares). Para hacer corta una historia larga y dolorosa, así es como lo arreglé:

Primero, debes capturar el IOError (Python 2) o BrokenPipeError (Python 3). Dependiendo de su progtwig, puede intentar salir temprano en ese momento o simplemente ignorar la excepción:

 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 

Sin embargo, esto no es suficiente. Python 3 todavía puede imprimir un mensaje como este:

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

Desafortunadamente, deshacerme de ese mensaje no es sencillo, pero finalmente encontré http://bugs.python.org/issue11380 donde Robert Collins sugiere esta solución que convertí en un decorador con el que puedes envolver tu función principal (sí, es una locura). sangría):

 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 

Esto también puede ocurrir si el final de lectura de la salida de su script muere prematuramente

es decir, open.py | otro Comando

si otherCommand sale y open.py intenta escribir en stdout

Tuve un guión malo que me hizo esta hermosa.

Sé que esta no es la forma “correcta” de hacerlo, pero si simplemente está interesado en deshacerse del mensaje de error, puede intentar esta solución:

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

Los cierres se deben hacer en orden inverso al de las aperturas.