J'ai un script très simple 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()
Mais on dit toujours:
IOError: [Errno 32] Broken pipe
J'ai vu sur Internet toutes les manières compliquées de résoudre ce problème, mais j'ai copié ce code directement, alors je pense qu'il y a un problème avec le code et pas avec SIGPIPE de Python.
Je redirige la sortie, donc si le script ci-dessus s'appelait "open.py", ma commande à exécuter serait la suivante:
open.py | othercommand
Je n'ai pas reproduit le problème, mais cette méthode pourrait peut-être le résoudre: (écrire ligne par ligne dans stdout
plutôt que d'utiliser print
)
import sys
with open('a.txt', 'r') as f1:
for line in f1:
sys.stdout.write(line)
Vous pourriez attraper le tuyau cassé? Ceci écrit le fichier dans stdout
ligne par ligne jusqu'à ce que le canal soit fermé.
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
Vous devez également vous assurer que othercommand
lit le tuyau avant qu'il ne devienne trop gros - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
Une erreur "Pipe cassée" se produit lorsque vous essayez d'écrire dans un tuyau qui a été fermé à l'autre extrémité. Étant donné que le code que vous avez montré ne concerne aucun canal directement, je suppose que vous faites quelque chose en dehors de Python pour rediriger la sortie standard de l'interpréteur Python vers un autre emplacement. Cela peut arriver si vous utilisez un script comme celui-ci:
python foo.py | someothercommand
Le problème que vous avez est que someothercommand
se ferme sans lire tout ce qui est disponible sur son entrée standard. Votre écriture (via print
) échouera à un moment donné.
J'ai pu reproduire l'erreur avec la commande suivante sur un système Linux:
python -c 'for i in range(1000): print i' | less
Si je ferme le téléavertisseur less
sans faire défiler toutes ses entrées (1 000 lignes), Python se termine avec la même IOError
que vous avez signalée.
Je me sens obligé de signaler que la méthode utilisant
signal(SIGPIPE, SIG_DFL)
est en effet dangereux (comme l'a déjà suggéré David Bennet dans les commentaires) et, dans mon cas, a conduit à une activité amusante dépendant de la plate-forme lorsqu'il est combiné à multiprocessing.Manager
(car la bibliothèque standard repose sur le fait que BrokenPipeError est généré à plusieurs endroits). Pour résumer une histoire longue et douloureuse, voici comment je l'ai corrigée:
Tout d’abord, vous devez récupérer IOError
(Python 2) ou BrokenPipeError
(Python 3). Selon votre programme, vous pouvez essayer de quitter tôt à ce stade ou simplement ignorer l'exception:
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
Cependant, cela ne suffit pas. Python 3 peut toujours imprimer un message comme celui-ci:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe
Malheureusement, me débarrasser de ce message n’est pas évident, mais j’ai enfin trouvé http://bugs.python.org/issue11380 où Robert Collins suggère cette solution de contournement que j’ai transformée en décoratrice, vous pouvez envelopper votre fonction principale avec (oui , c’est une indentation folle):
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
Cela peut également se produire si la fin de la lecture de la sortie de votre script décède prématurément.
ie open.py | autreCommande
si otherCommand se ferme et open.py essaie d'écrire sur stdout
J'ai eu un mauvais script de gawk qui m'a fait cette belle.
Les fermetures doivent être effectuées dans l'ordre inverse des ouvertures.
Je sais que ce n'est pas la manière "appropriée" de le faire, mais si vous souhaitez simplement vous débarrasser du message d'erreur, vous pouvez essayer cette solution de contournement:
python your_python_code.py 2> /dev/null | other_command