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