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 Python.
я перенаправляю вывод, Поэтому, если вышеупомянутый скрипт был назван "open.py - тогда моя команда бежать будет:
open.py | othercommand
7 ответов
Я не воспроизвел проблему, но, возможно, этот метод решит ее: (запись строки за строкой в stdout
вместо print
)
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)
посмотреть здесь на фоне этого решения. Лучше отвечай!--6-->здесь.
вывести полезный ответ Алекса л., полезный ответ Ахана и полезный ответ Blckknght вместе с некоторой дополнительной информацией:
-
стандартный сигнал Unix
SIGPIPE
отправляется в процесс писать до труба когда нет процесса чтение из трубы (больше).- это не обязательно состояние, некоторые утилиты Unix, такие как
head
конструкция прекратите преждевременно читать из трубы, как только они получили достаточно данных.
- это не обязательно состояние, некоторые утилиты Unix, такие как
по умолчанию - то есть,если процесс записи явно не ловушка
SIGPIPE
- процесс записи просто расторгнут, и его код выхода установлен в141
, которая рассчитывается как128
(сигнализировать прекращение сигналом вообще)+13
(SIGPIPE
специфический сигнал ).по замыслу, однако, Python ловушки
SIGPIPE
и переводит его в PythonIOError
экземплярerrno
стоимостьюerrno.EPIPE
, так что скрипт Python может поймать его, если он так решит-см. ответ Алекса л. как это сделать.если a Питон скрипт тут не поймать его, Python выводит сообщение об ошибке
IOError: [Errno 32] Broken pipe
и завершает скрипт с помощью exit код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
выходит без чтения всего доступного на своем стандартном входном сигнале. Этот вызывает вашу запись (через print
), чтобы не в какой-то момент.
я смог воспроизвести ошибку со следующей командой в системе 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
Это также может произойти, если конец чтения вывода из вашего скрипта умирает преждевременно
ie open.py / otherCommand
Если otherCommand выходит и open.py пытается написать в stdout
У меня был плохой сценарий gawk, который сделал это прекрасным для меня.