IOError: [Errno 32] broken pipe: Python

j'ai un script Python 3 très simple:

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 il dit toujours:

IOError: [Errno 32] Broken pipe

j'ai vu sur internet toutes les façons compliquées de corriger cela, mais j'ai copié ce code directement, donc je pense qu'il y a quelque chose qui ne va pas avec le code et pas le SIGPIPE de Python.

je redirige la sortie, Donc si le script ci-dessus a été nommé "open.py", alors ma commande à exécuter serait:

open.py | othercommand
61
demandé sur ks1322 2013-01-08 07:18:28

7 réponses

Je n'ai pas reproduit la question, mais peut-être cette méthode résoudrait-elle: (écrire ligne par ligne à 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 dossier à stdout ligne par ligne jusqu'à ce que la pipe soit fermée.

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 est la lecture de la pipe avant qu'elle ne devienne trop grande - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

25
répondu Alex L 2017-04-13 12:36:27

le problème est dû à la manipulation 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 le fond sur cette solution. Mieux répondre ici .

102
répondu akhan 2018-08-29 06:38:56

apporter Alex L. de réponse utile , akhan de réponse utile , et Blckknght de réponse utile avec quelques informations supplémentaires:

  • Standard Unix signal SIGPIPE est envoyé à un processus écriture à un pipe quand il n'y a pas de processus lire de la pipe (ou plus).

    • ce n'est pas nécessairement une erreur condition; certains utilitaires Unix tels que head par conception arrêter de lire prématurément à partir d'une pipe, une fois qu'ils ont reçu suffisamment de données.
  • par défaut - i.e., si le processus d'écriture n'est pas explicitement piège SIGPIPE - le processus d'écriture est simplement terminé , et son code de sortie est réglé à 141 , qui est calculé comme 128 (à la fin du signal par signal en général) + 13 ( SIGPIPE 's signal spécifique numéro ).

  • de Par sa conception, cependant, Python lui-même traps SIGPIPE et le traduit en un python IOError instance avec errno valeur errno.EPIPE , de sorte qu'un script Python peut l'attraper, s'il le souhaite - voir la réponse D'Alex L. pour savoir comment faire cela.

  • Si un Python script ne pas l'attraper , Python erreur sur les sorties le message IOError: [Errno 32] Broken pipe et termine le script avec le code de sortie 1 - c'est le symptôme que L'OP a vu.

  • Dans de nombreux cas c'est plus d'inconvénients que d'utile , donc de revenir au comportement par défaut est souhaitable :

    • utilisant le signal le module permet justement cela, comme indiqué dans la réponse d'akhan ; signal.signal() prend un signal à manipuler comme premier argument et un handler comme deuxième; valeur de handler spéciale SIG_DFL représente le système par défaut comportement:

      from signal import signal, SIGPIPE, SIG_DFL
      signal(SIGPIPE, SIG_DFL) 
      
60
répondu mklement0 2017-05-23 12:34:47

une erreur de "Pipe cassée" se produit lorsque vous essayez d'écrire à une pipe qui a été fermée à l'autre extrémité. Puisque le code que vous avez montré n'implique pas de pipes directement, je soupçonne que vous faites quelque chose en dehors de Python pour rediriger la sortie standard de L'interpréteur Python vers un autre endroit. Cela peut arriver si vous exécutez un script comme celui-ci:

python foo.py | someothercommand

le problème que vous avez est que someothercommand sort sans lire tout disponible sur son entrée standard. Cela provoque l'échec de votre écriture (via print ) à 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 pager less sans faire défiler toutes ses entrées (1000 lignes), Python sort avec le même IOError que vous avez signalé.

23
répondu Blckknght 2013-01-08 11:10:14

je me sens obligé de souligner que la méthode utilisant

signal(SIGPIPE, SIG_DFL) 

est en effet dangereux (comme déjà suggéré par David Bennet dans les commentaires) et dans mon cas a conduit à plate-forme-dépendante funny business lorsqu'il est combiné avec multiprocessing.Manager (parce que la bibliothèque standard repose sur BrokenPipeError étant soulevé à plusieurs endroits). Pour faire court à une histoire longue et douloureuse, voici comment je l'ai corrigé:

D'abord, vous devez attrapez le IOError (Python 2) ou BrokenPipeError (Python 3). Selon votre programme, vous pouvez essayer un départ anticipé à ce point ou tout 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 encore imprimer un message comme celui-ci:

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

malheureusement, se débarrasser de ce message n'est pas simple, mais j'ai finalement trouvé http://bugs.python.org/issue11380 où Robert Collins suggère ce contournement que j'ai transformé en décorateur, 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
15
répondu trehn 2016-03-03 20:05:39

cela peut également se produire si la fin de la lecture de la sortie de votre script meurt prématurément

ie open.py 151910920"

si une autre marque open.py essaie d'écrire à stdout

j'ai eu un mauvais script gawk qui m'a fait cette belle à moi.

1
répondu lkreinitz 2013-10-17 18:23:24

Ferme doit être fait dans l'ordre inverse de l'ouvre.

0
répondu Paul 2015-01-28 10:19:59