web-dev-qa-db-fra.com

IOError: [Errno 32] Tube cassé: Python

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
66
JOHANNES_NYÅTT

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

32
Alex L

Le problème est dû à la gestion de SIGPIPE. Vous pouvez résoudre ce problème en utilisant le code suivant:

from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL) 

Voir ici pour l'historique de cette solution. Meilleure réponse ici .

106
akhan

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.

24
Blckknght

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
17
trehn

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.

1
lkreinitz

Les fermetures doivent être effectuées dans l'ordre inverse des ouvertures.

0
Paul

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
0
ssanch