web-dev-qa-db-fra.com

Comment puis-je réparer une erreur de tuyau cassé?

J'ai récemment réinstallé RVM (en suivant les instructions de http://rvm.io ) après une nouvelle installation d'Ubuntu 12.10 avec un lecteur SSD.

Maintenant, quand je tape: type rvm | head -1

Je reçois l'erreur suivante:

rvm is a function
-bash: type: write error: Broken pipe

Mais si je répète immédiatement la commande, je ne reçois que:

rvm is a function

Et il semble que tout va bien? Que ce passe-t-il? Que puis-je faire pour le réparer? Cela n'arrive pas toujours. Cela semble être plus sporadique. J'ai essayé de trouver un modèle, mais je ne l'ai pas encore fait.

32
Jason Shultz

Voir "Pipe cassée" dans cette situation est rare, mais normal.

Lorsque vous exécutez type rvm | head -1, bash exécute type rvm dans un processus, head -1 dans un autre.1 La sortie standard de type est connectée à la fin "write" d'un pipe , le stdin de head à la fin "read". Les deux processus s'exécutent en même temps.

Le processus head -1 lit les données de stdin (généralement par tranches de 8 Ko), imprime une seule ligne (selon l'option -1) et se termine, ce qui provoque la fermeture de l'extrémité "lecture" du canal. Étant donné que la fonction rvm est assez longue (environ 11 Ko après avoir été analysée et reconstruite par bash), cela signifie que head se ferme alors que type a encore quelques ko de données à écrire.

À ce stade, puisque type tente d'écrire sur un tuyau dont l'autre extrémité a été fermée - un cassé tuyau - la fonction write () qu'elle a appelée renvoie une erreur EPIPE, traduite par "Tube cassé". En plus de cette erreur, le noyau envoie également le signal SIGPIPE à type, qui par défaut tue le processus immédiatement.

(Le signal est très utile dans les shells interactifs, car la plupart des utilisateurs ne veulent pas que le premier processus continue à courir et tente d'écrire nulle part. Les services non interactifs ignorent SIGPIPE - ce ne serait pas bon pour un démon long mourez avec une erreur aussi simple - ils trouvent donc le code d'erreur très utile.)

Cependant, la livraison du signal n'est pas immédiate à 100% et il peut arriver que write () renvoie EPIPE et que le processus continue de s'exécuter pendant un court instant avant de recevoir le signal. Dans ce cas, type dispose de suffisamment de temps pour détecter l’échec de l’écriture, traduire le code d’erreur et même imprimer un message d’erreur sur stderr avant d’être éliminé par SIGPIPE. (Le message d'erreur indique "-bash: type:" puisque type est une commande intégrée de bash lui-même.)

Cela semble être plus courant sur les systèmes multiprocesseurs, car le processus type et le code de livraison du signal du noyau peuvent être exécutés sur différents cœurs, littéralement en même temps.

Il serait possible de supprimer ce message en corrigeant la variable type (dans le code source de bash) pour qu'elle se ferme immédiatement lorsqu'elle reçoit un EPIPE de la fonction write ().

Cependant, il n'y a pas de quoi s'inquiéter, et cela n'est en aucun cas lié à votre installation rvm.

55
grawity

Vous pouvez réparer un tuyau cassé au détriment d'un autre processus en insérant tail -n +1 dans votre tuyau, comme ceci:

tapez rvm | queue -n +1 | tête -1

Le +1 indique à tail d'imprimer la première ligne d'entrée et tout ce qui suit. La sortie sera exactement la même que si tail -n +1 n'y était pas, mais le programme est suffisamment intelligent pour vérifier la sortie standard et ferme le canal proprement. Pas plus tuyaux cassés .

23
Huuu

Le message write error: Broken pipe fait référence à un processus d'écriture qui tente d'écrire sur un canal sans laisser de lecteur à la fin de la lecture de ce canal et en tenant compte du fait que le signal SIGPIPE doit être ignoré par le processus actuel ou parent. Si c'est le processus parent qui a défini SIGPIPE à ignorer, il n'est pas possible pour le processus enfant de l'annuler à nouveau dans un shell non interacitif.

Cependant, il est possible de tuer type rvm lorsque head -1 se termine en utilisant des sous-shell explicites. De cette façon, nous pouvons utiliser type rvm en arrière-plan, envoyer typepid dans le sous-shell head -1, puis implémenter une interruption sur EXIT afin de tuer type rvm explicitement.

trap "" PIPE        # parent process sets SIGPIPE to be ignored
bash                # start child process
export LANG=C
# create a fake rvm function
eval "
rvm() {
$(printf 'echo line of rvm code %s\n' {1..10000})
}
"

# rvm is a function
# bash: type: write error: Broken pipe
type rvm | head -1

# kill type rvm when head -1 terminates
# sleep 0: do nothing but with external command
( (sleep 0; type rvm) & echo ${!} ; wait ${!} ) | 
    (trap 'trap - EXIT; kill "$typepid"; exit' EXIT; typepid="$(head -1)"; head -1)
2
zancox