IOError: [Errno 32] сломанная труба: Python
у меня есть очень простой скрипт Python 3:
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()
но он всегда говорит:
IOError: [Errno 32] Broken pipe
Я видел в интернете все сложные способы исправить это, но я скопировал этот код напрямую, поэтому я думаю, что там что-то не так с кодом, а не сигнал sigpipe, питона.
я перенаправляю вывод, так что если выше скрипт был назван "open.py", тогда моя команда бежать будет:
open.py | othercommand
7 ответов:
Я не воспроизвел проблему, но, возможно, этот метод решит ее: (запись строки за строкой в
stdout
вместоimport sys with open('a.txt', 'r') as f1: for line in f1: sys.stdout.write(line)
вы могли бы поймать сломанную трубу? Это записывает файл в
stdout
построчно, пока труба не будет закрыта.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
вы также должны убедиться, что
othercommand
читает из трубы, прежде чем он получает слишком большие - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
проблема связана с обработкой SIGPIPE. Вы можете решить эту проблему, используя следующий код:
from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)
посмотреть здесь на фоне этого решения. Лучше ответь здесь.
вывести полезный ответ Алекса л., полезный ответ Ахана и полезный ответ Blckknght вместе с некоторой дополнительной информацией:
стандартный сигнал Unix
SIGPIPE
отправляется в процесс писать до труба когда нет процесса чтение из трубы (больше).
- это не обязательно состояние, некоторые утилиты Unix, такие как
head
конструкция прекратите преждевременно читать из трубы, как только они получили достаточно данных.по умолчанию - то есть,если процесс записи явно не ловушка
SIGPIPE
- процесс записи просто расторгнут, и его код выхода установлен в141
, которая рассчитывается как128
(для завершения сигнала сигналом в целом)+13
(SIGPIPE
специфический сигнал ).по замыслу, однако,Python ловушки
SIGPIPE
и переводит его в PythonIOError
экземплярerrno
стоимостьюerrno.EPIPE
, так что скрипт Python может поймать его, если он так хочет-см. ответ Алекса л. как это сделать.если a Питон скрипт тут не поймать его питон выводит сообщение об ошибке
IOError: [Errno 32] Broken pipe
и завершает работу скрипта с кодом выхода1
- это симптом, который увидел ОП.во многих случаях это более разрушительным, чем полезным, так что желательно вернуться к поведению по умолчанию:
использование
signal
модуль позволяет именно это, как указано в ответ Ахана;signal.signal()
принимает сигнал для обработки в качестве 1-го аргумента и обработчик в качестве 2-го; специальное значение обработчикаSIG_DFL
представляет системы по умолчанию поведение:from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
ошибка "сломанная труба" возникает при попытке записи в трубу, которая была закрыта на другом конце. Поскольку код, который вы показали, не включает в себя никаких каналов напрямую, я подозреваю, что вы делаете что-то за пределами Python, чтобы перенаправить стандартный вывод интерпретатора Python в другое место. Это может произойти, если вы используете такой скрипт:
python foo.py | someothercommand
проблема у вас есть, что
someothercommand
выходит, не читая все, что доступно на его стандартном входе. Этот вызывает вашу запись (черезя смог воспроизвести ошибку с помощью следующей команды в системе Linux:
python -c 'for i in range(1000): print i' | less
если я закрою
less
пейджер без прокрутки всех его входных данных (1000 строк), Python выходит с тем жеIOError
вы сообщили.
Я чувствую себя обязанным указать, что метод с помощью
signal(SIGPIPE, SIG_DFL)
действительно опасно (как уже предлагал Дэвид Беннет в комментариях) и в моем случае привел к зависящему от платформы смешному бизнесу в сочетании с
multiprocessing.Manager
(потому что стандартная библиотека полагается на BrokenPipeError поднимается в нескольких местах). Чтобы сделать длинную и болезненную историю короткой, вот как я ее исправил:во-первых, вы должны поймать
IOError
(Python 2) илиBrokenPipeError
(Python 3). В зависимости от вашей программы вы можете попытаться выйти рано в этот момент или просто игнорировать исключение: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
однако, этого недостаточно. Python 3 может по-прежнему печатать сообщение следующего вида:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> BrokenPipeError: [Errno 32] Broken pipe
к сожалению, избавиться от этого сообщения не так просто, но я, наконец, нашел http://bugs.python.org/issue11380 где Роберт Коллинз предлагает этот обходной путь, который я превратил в декоратора, вы можете обернуть свою основную функцию (да, это какой-то сумасшедший отступ):
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