Eu tenho um script Python 3 muito simples:
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()
Mas sempre diz:
IOError: [Errno 32] Broken pipe
Eu vi na internet todas as maneiras complicadas de consertar isso, mas copiei esse código diretamente, então acho que há algo errado com o código e não com o SIGPIPE do Python.
Estou redirecionando a saída, portanto, se o script acima foi denominado "open.py", meu comando para executar seria:
open.py | othercommand
python
python-3.x
sigpipe
JOHANNES_NYÅTT
fonte
fonte
print(f1.readlines())
a.txt
e uma gravação parastdout
. Talvez tente dividi-los em linhas separadas para ver qual operação dispara a exceção. Sestdout
for um tubo e a extremidade de leitura tiver sido fechada, isso pode explicar oEPIPE
erro.print
chamada seja a culpada. @ JOHANNES_NYÅTT, você pode esclarecer como está iniciando seu script Python? Você está redirecionando a saída padrão para algum lugar?Respostas:
Não reproduzi o problema, mas talvez este método resolva: (escrever linha por linha em
stdout
vez de usarprint
)import sys with open('a.txt', 'r') as f1: for line in f1: sys.stdout.write(line)
Você poderia pegar o cano quebrado? Isso grava o arquivo
stdout
linha por linha até que o tubo seja fechado.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
Você também precisa ter certeza de que
othercommand
está lendo o tubo antes que fique muito grande - /unix/11946/how-big-is-the-pipe-bufferfonte
print
chamada, não com a leitura dos arquivos).O problema é devido ao manuseio do SIGPIPE. Você pode resolver esse problema usando o seguinte código:
from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)
Veja aqui o histórico desta solução. Melhor resposta aqui .
fonte
Para trazer a resposta útil de Alex L. , a resposta útil de akhan e a resposta útil de Blckknght junto com algumas informações adicionais:
O sinal Unix padrão
SIGPIPE
é enviado para um processo de gravação em um pipe quando não há mais leitura de processo no pipe (mais).head
por exemplo, param de ler prematuramente de um tubo, uma vez que recebem dados suficientes.Por padrão - ou seja, se o processo de gravação não capturar explicitamente
SIGPIPE
- o processo de gravação é simplesmente encerrado e seu código de saída é definido como141
, que é calculado como128
(para sinalizar o encerramento por sinal em geral) +13
( número deSIGPIPE
sinal específico de ) .Por design, no entanto, o próprio Python o captura
SIGPIPE
e traduz em umaIOError
instância do Python comerrno
valorerrno.EPIPE
, de forma que um script Python possa capturá-lo, se assim o desejar - veja a resposta de Alex L. para saber como fazer isso.Se um Python roteiro se não pegá-lo , Python mensagem de erro saídas
IOError: [Errno 32] Broken pipe
e termina o script com o código de saída1
- este é o sintoma da serra OP.Em muitos casos, isso é mais perturbador do que útil , portanto, reverter para o comportamento padrão é desejável :
Usar o
signal
módulo permite exatamente isso, conforme declarado na resposta de akhan ;signal.signal()
recebe um sinal para manipular como o primeiro argumento e um manipulador como o segundo; o valor do manipulador especialSIG_DFL
representa o comportamento padrão do sistema :from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
fonte
Um erro "Tubo quebrado" ocorre quando você tenta gravar em um tubo que foi fechado na outra extremidade. Como o código que você mostrou não envolve nenhum canal diretamente, suspeito que você esteja fazendo algo fora do Python para redirecionar a saída padrão do interpretador Python para outro lugar. Isso pode acontecer se você estiver executando um script como este:
O problema que você tem é
someothercommand
sair sem ler tudo o que está disponível em sua entrada padrão. Isso faz com que sua gravação (viaprint
) falhe em algum ponto.Consegui reproduzir o erro com o seguinte comando em um sistema Linux:
python -c 'for i in range(1000): print i' | less
Se eu fechar o
less
pager sem rolar por todas as suas entradas (1000 linhas), o Python sai com o mesmo queIOError
você relatou.fonte
SIGPIPE
sinal não indica necessariamente uma condição de erro ; alguns utilitários Unix, notavelmentehead
, por design, durante a operação normal fecham o tubo cedo, uma vez que eles já leu tanto de dados como eles necessário.Sinto-me na obrigação de salientar que o método que utiliza
é realmente perigoso (como já sugerido por David Bennet nos comentários) e, no meu caso, levou a negócios engraçados dependentes da plataforma quando combinado com
multiprocessing.Manager
(porque a biblioteca padrão depende de BrokenPipeError sendo criado em vários lugares). Para encurtar uma longa e dolorosa história, é assim que eu consertei:Primeiro, você precisa capturar o
IOError
(Python 2) ouBrokenPipeError
(Python 3). Dependendo do seu programa, você pode tentar sair mais cedo nesse ponto ou simplesmente ignorar a exceção: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
No entanto, isso não é suficiente. O Python 3 ainda pode imprimir uma mensagem como esta:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> BrokenPipeError: [Errno 32] Broken pipe
Infelizmente, livrar-se dessa mensagem não é simples, mas finalmente encontrei http://bugs.python.org/issue11380 onde Robert Collins sugere esta solução alternativa que transformei em um decorador com a qual você pode embrulhar sua função principal (sim, isso é uma loucura recuo):
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
fonte
Sei que essa não é a maneira "correta" de fazer isso, mas se você estiver simplesmente interessado em se livrar da mensagem de erro, pode tentar esta solução alternativa:
python your_python_code.py 2> /dev/null | other_command
fonte
A primeira resposta (
if e.errno == errno.EPIPE:
) aqui realmente não funcionou para mim. Eu tenho:AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'
No entanto, isso deve funcionar se você só se preocupa em ignorar tubos quebrados em gravações específicas. Acho que é mais seguro do que capturar SIGPIPE:
try: # writing, flushing, whatever goes here except BrokenPipeError: exit( 0 )
Obviamente, você tem que tomar uma decisão sobre se o seu código está realmente pronto se você acertar o tubo quebrado, mas para a maioria dos propósitos, acho que isso geralmente será verdade. (Não se esqueça de fechar os identificadores de arquivo, etc.)
fonte
Isso também pode ocorrer se o final de leitura da saída de seu script morrer prematuramente
ou seja, open.py | otherCommand
se otherCommand sai e open.py tenta escrever para stdout
Eu tinha um script gawk ruim que me fez isso adorável.
fonte
head
, pelo projeto, durante a operação normal perto do tubo cedo, uma vez que li o máximo de dados que necessário. A maioria das CLIs simplesmente adia o sistema por seu comportamento padrão: encerrar silenciosamente o processo de leitura e relatar o código de saída141
(o que, em um shell, não é imediatamente aparente, porque o último comando de um pipeline determina o código de saída geral). O comportamento padrão do Python , infelizmente, é morrer ruidosamente .Os fechamentos devem ser feitos na ordem inversa das aberturas.
fonte