IOError:[Errno 32] Broken pipe: Python
python-3.x sigpipe (7)
El problema se debe al manejo de SIGPIPE. Puede resolver este problema usando el siguiente código:
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL)
Vea aquí para obtener antecedentes sobre esta solución.
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 arreglar 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 redireccionando el resultado, por lo que si el script anterior se llamara "open.py", entonces mi comando para ejecutar sería:
open.py | othercommand
Esto también puede ocurrir si el final de lectura del resultado de su script muere prematuramente
es decir, open.py | otherCommand
si otherCommand sale y open.py intenta escribir en stdout
Tenía un guión malo que me hizo esto encantador.
Los cierres deben hacerse en orden inverso al de las aperturas.
Me siento obligado a señalar que el método de uso
signal(SIGPIPE, SIG_DFL)
es realmente 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
Administrador (porque la biblioteca estándar se basa en que BrokenPipeError se generó en varios lugares). Para resumir una historia larga y dolorosa, así es como lo arreglé:
En primer lugar, debe detectar IOError
(Python 2) o BrokenPipeError
(Python 3). Dependiendo de su programa, puede intentar salir temprano en ese punto 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
Lamentablemente, 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 decoradora con la que puedes envolver tu función principal (sí, eso es un poco loco 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
No he reproducido el problema, pero tal vez este método lo resolvería: (escribiendo línea por línea para stdout
lugar de usar print
)
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 en stdout
línea por línea hasta que se cierre 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 antes de que se othercommand
demasiado grande - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
Para traer la respuesta útil de Alex L., la respuesta útil de Akhan y la útil respuesta de Blckknght junto con información adicional:
La señal estándar de Unix,
SIGPIPE
se envía a un proceso de escritura en una pipe cuando ya no hay lectura de proceso desde la tubería (más).- Esto no es necesariamente una condición de error ; algunas utilidades de Unix, como la
head
por diseño, dejan de leer prematuramente desde una tubería, una vez que han recibido suficientes datos.
- Esto no es necesariamente una condición de error ; algunas utilidades de Unix, como la
Por defecto , es decir, si el proceso de escritura no atrapa
SIGPIPE
explícitamente, el proceso de escritura simplemente finaliza y su código de salida se establece en141
, que se calcula como128
(para terminar la señal por señal en general) +13
(SIGPIPE
'' s número de señal específico).Sin embargo, por diseño, Python atrapa a
SIGPIPE
y lo traduce a una instancia de PythonIOError
conerrno
valueerrno.EPIPE
, de modo que un script de Python puede atraparlo, si así lo desea, ver la respuesta de Alex L. sobre cómo hacerlo .Si un script de Python no lo
IOError: [Errno 32] Broken pipe
, Python genera el mensaje de errorIOError: [Errno 32] Broken pipe
y finaliza el script con el 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 :
Usar el módulo de
signal
permite 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; valor especial del controladorSIG_DFL
representa el comportamiento predeterminado del sistema:from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
Se produce un error de "Rotura de tubería" 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 canalización 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:
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 buscapersonas less
sin desplazarme por toda su entrada (1000 líneas), Python sale con el mismo IOError
que ha informado.