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