Tengo un script 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 el script anterior se llamara "open.py", entonces mi comando para ejecutar sería:
open.py | othercommand
python
python-3.x
sigpipe
JOHANNES_NYÅTT
fuente
fuente
print(f1.readlines())
a.txt
y una escritura haciastdout
. Quizás intente dividirlos en líneas separadas para que pueda ver qué operación desencadena la excepción. Sistdout
es una tubería y el extremo de lectura se ha cerrado, entonces eso podría explicar elEPIPE
error.print
llamada es la culpable. @ JOHANNES_NYÅTT, ¿puede aclarar cómo está iniciando su secuencia de comandos de Python? ¿Está redirigiendo la salida estándar a alguna parte?Respuestas:
No he reproducido el problema, pero tal vez este método lo resolvería: (escribiendo línea por línea en
stdout
lugar de usarprint
)import sys with open('a.txt', 'r') as f1: for line in f1: sys.stdout.write(line)
¿Podrías atrapar la tubería rota? Esto escribe el archivo
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
othercommand
leer desde la tubería antes de que sea demasiado grande: /unix/11946/how-big-is-the-pipe-bufferfuente
print
llamada, no con la lectura de los archivos).El problema se debe al manejo de SIGPIPE. Puede resolver este problema utilizando el siguiente código:
from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)
Consulte aquí los antecedentes de esta solución. Mejor respuesta aquí .
fuente
Para traer la respuesta de Alex L. útil , respuesta útil de Akhan y respuesta útil de Blckknght junto con alguna información adicional:
La señal Unix estándar
SIGPIPE
se envía a un proceso que escribe en una tubería cuando ya no hay un proceso que lea desde la tubería.head
por ejemplo , dejan de leer prematuramente desde una tubería, una vez que han recibido suficientes datos.De forma predeterminada , es decir, si el proceso de escritura no intercepta explícitamente
SIGPIPE
, el proceso de escritura simplemente se termina y su código de salida se establece en141
, que se calcula como128
(para señalar la terminación por señal en general) +13
( número deSIGPIPE
señal específico ) .Sin embargo, por diseño, Python lo atrapa
SIGPIPE
y lo traduce a unaIOError
instancia de Python conerrno
valorerrno.EPIPE
, de modo que una secuencia de comandos de Python pueda atraparlo, si así lo desea; consulte la respuesta de Alex L. para saber cómo hacerlo.Si un script de Python no lo detecta , Python genera un mensaje de error
IOError: [Errno 32] Broken pipe
y termina el script con un código de salida1
; este es el síntoma que vio el OP.En muchos casos, esto es más perturbador que útil , por lo que es deseable volver al comportamiento predeterminado :
El uso del
signal
módulo permite precisamente eso, como se indica en la respuesta de Akhan ;signal.signal()
toma una señal para manejar como primer argumento y un manejador como segundo; El valor del controlador especialSIG_DFL
representa el comportamiento predeterminado del sistema :from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
fuente
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 ha mostrado no involucra ninguna tubería directamente, sospecho que está haciendo algo fuera de Python para redirigir la salida estándar del intérprete de Python a otro lugar. Esto podría suceder si está ejecutando un script como este:
El problema que tiene es que
someothercommand
sale sin leer todo lo disponible en su entrada estándar. Esto hace que su escritura (víaprint
) 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
less
buscapersonas sin desplazarme por todas sus entradas (1000 líneas), Python sale con lo mismoIOError
que ha informado.fuente
SIGPIPE
señal no indica necesariamente una condición de error ; algunas utilidades de Unix, en particularhead
, por diseño, durante el funcionamiento normal cierran la tubería temprano, una vez que se han leído todos los datos que se necesitan.Me siento obligado a señalar que el método que utiliza
es de hecho peligroso (como ya sugirió David Bennet en los comentarios) y en mi caso condujo a negocios divertidos dependientes de la plataforma cuando se combinó con
multiprocessing.Manager
(porque la biblioteca estándar se basa en BrokenPipeError que se genera en varios lugares). Para resumir una historia larga y dolorosa, así es como lo arreglé:Primero, debe capturar el
IOError
(Python 2) oBrokenPipeError
(Python 3). Dependiendo de su programa, 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 aún puede imprimir un mensaje como este:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> BrokenPipeError: [Errno 32] Broken pipe
Desafortunadamente, deshacerse 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í, eso 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
fuente
Sé que esta no es la forma "correcta" de hacerlo, pero si simplemente está interesado en deshacerse del mensaje de error, puede probar esta solución alternativa:
python your_python_code.py 2> /dev/null | other_command
fuente
La respuesta principal (
if e.errno == errno.EPIPE:
) aquí realmente no funcionó para mí. Tengo:AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'
Sin embargo, esto debería funcionar si lo único que le importa es ignorar las tuberías rotas en escrituras específicas. Creo que es más seguro que atrapar SIGPIPE:
try: # writing, flushing, whatever goes here except BrokenPipeError: exit( 0 )
Obviamente, tienes que tomar una decisión sobre si tu código está realmente hecho si te encuentras con la tubería rota, pero para la mayoría de los propósitos, creo que eso suele ser cierto. (No olvide cerrar los identificadores de archivos, etc.)
fuente
Esto también puede ocurrir si el final de lectura de la salida de su script muere prematuramente
es decir, open.py | otherCommand
si sale otherCommand y open.py intenta escribir en stdout
Tenía un guión malísimo que me hizo esto de maravilla.
fuente
head
, por diseño, durante el funcionamiento normal cierran la tubería temprano, una vez que han leído todos los datos que necesitaban. La mayoría de las CLI simplemente ceden al sistema por su comportamiento predeterminado: terminar silenciosamente el proceso de lectura y reportar el código de salida141
(que, en un shell, no es evidente, porque el último comando de una canalización determina el código de salida general). El comportamiento predeterminado de Python , desafortunadamente, es morir ruidosamente .Los cierres deben realizarse en orden inverso al de las aperturas.
fuente