Comment puis-je corriger une erreur de canal cassé?

36

J'ai récemment réinstallé RVM (en suivant les instructions sur http://rvm.io ) après une nouvelle installation d'Ubuntu 12.10 lorsque j'ai obtenu un disque 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 plus sporadique. J'ai essayé de lui trouver une sorte de modèle, mais je ne l'ai pas encore fait.

Jason Shultz
la source

Réponses:

57

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

Lorsque vous exécutez type rvm | head -1, bash s'exécute type rvmdans un processus, head -1dans un autre. 1 La sortie standard de typeest connectée à l'extrémité "écriture" d'un tuyau , la sortie standard headà l'extrémité "lecture". Les deux processus s'exécutent en même temps.

Le head -1processus lit les données de stdin (généralement par blocs de 8 Ko), imprime une seule ligne (selon l' -1option) et se ferme, provoquant la fermeture de l'extrémité "lecture" du tuyau. Étant donné que la rvmfonction est assez longue (environ 11 kB après avoir été analysée et reconstruite par bash), cela signifie que la headsortie typea encore quelques kB de données à écrire.

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

(Le signal est très utile dans les shells interactifs, car la plupart des utilisateurs ne veulent pas que le premier processus continue de fonctionner et essaie d'écrire nulle part. Pendant ce temps, les services non interactifs ignorent SIGPIPE - il ne serait pas bon pour un démon de longue durée de mourir sur une erreur aussi simple - ils trouvent donc le code d'erreur très utile.)

Cependant, la livraison du signal n'est pas 100% immédiate, et il peut y avoir des cas où write () renvoie EPIPE et le processus continue de s'exécuter pendant un court moment avant de recevoir le signal. Dans ce cas, typeobtient suffisamment de temps pour remarquer l'échec de l'écriture, traduire le code d'erreur et même imprimer un message d'erreur à stderr avant d'être tué par SIGPIPE. (Le message d'erreur indique "-bash: type:" car il types'agit d'une commande intégrée de bash lui-même.)

Cela semble être plus courant sur les systèmes multi-CPU, car le typeprocessus et le code de livraison du signal du noyau peuvent s'exécuter sur différents cœurs, littéralement en même temps.

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

Cependant, il n'y a rien à craindre et ce n'est en aucun cas lié à votre rvminstallation.

Grawity
la source
Merci! J'en étais inquiet. J'ai fait environ une heure de recherche sur Google hier soir pour dépanner mon installation RVM et y faire des réparations en essayant de le réparer. Je venais d'échanger avec un disque SSD et d'utiliser LVM et de chiffrer le disque dur, donc il y avait beaucoup de variables entrant en jeu et je ne savais tout simplement pas ce qui aurait pu se passer latéralement. Merci d'avoir mis mon esprit à l'aise!
Jason Shultz
Je diffuse la sortie de lsthrough head -1depuis des années, et aujourd'hui je reçois un message de pipe cassé.
Tulains Córdova
1
(Remarque: l'erreur "Broken pipe" ne provient pas du signal. Elle provient de l' errno . Bien que le shell puisse sinon afficher des messages texte pour les sorties induites par le signal, il est généralement assez intelligent pour prétendre qu'une sortie SIGPIPE était un `` propre ''.)
grawity
23

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

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

Le +1dit taild'imprimer la première ligne d'entrée et tout ce qui suit. La sortie sera exactement la même que si elle tail -n +1n'était pas là, mais le programme est assez intelligent pour vérifier la sortie standard et ferme proprement le tuyau. Plus de tuyaux cassés .

Huuu
la source
1
Joli tour. J'ai utilisé dans une situation différente de celle fournie ici. Merci!
Quelqu'un vous utilise toujours MS-DOS le
6
Cela ressemblait à une excellente solution, mais sur Ubuntu 14.04.2 avec la queue 8.21, je reçois "tail: write error: Broken pipe", ce qui n'est pas une amélioration.
Roger Dueck
2
@RogerDueck est correct. Je vois également cela sur un système Mandriva pour un type similaire de problème qui se find /var/lib/mysql -xdev -type f -daystart -mmin +5 -print0 | xargs -0 ls -ldt | tail -n +1 | headproduit de manière fiable xargs: ls: terminated by signal 13. Comme nous le savons, le problème est celui de l'épuisement des entrées et il n'y a vraiment qu'une seule commande qui traite de la mise en mémoire tampon: dd. L'ajout | dd obs=1Mau pipeline corrige le SIGPIPE pour mon cas d'utilisation.
Andrew Beals
3
Je modifierai encore ma suggestion, bien que je note que je ne pense pas que les xargs ou le type devraient être kvetching sur SIGPIPE, à ceci: type rvm | (head -1 ; dd of=/dev/null) Ceci, bien sûr, est similaire à d'autres suggestions car il provoque le traitement de toutes les entrées , mais dddevrait être le programme le plus efficace pour gérer de telles choses.
Andrew Beals
3
Commentaire sur les contrevenants SIGPIPE ici: mail-index.netbsd.org/tech-userlevel/2013/01/07/msg007110.html
Andrew Beals
2

Le write error: Broken pipemessage fait référence à un processus d'écriture qui essaie d'écrire sur un canal sans aucun lecteur à l'extrémité de lecture de ce canal et à la circonstance spéciale que le SIGPIPEsignal est défini pour être ignoré par le processus actuel ou parent. Si c'est le processus parent qui a été défini SIGPIPEpour être ignoré, il n'est pas possible pour le processus enfant de l'annuler à nouveau dans un shell non interactif.

Cependant, il est possible de tuer une type rvmfois head -1terminé en utilisant des sous-coquilles explicites. De cette façon, nous pouvons créer un arrière type rvm- plan , envoyer typepidau head -1sous - shell, puis implémenter un piège EXITlà-bas pour tuer type rvmexplicitement.

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)
zancox
la source
De la réponse de grawity: typeobtient suffisamment de temps pour remarquer l'échec de l'écriture, traduire le code d'erreur et même imprimer un message d'erreur à stderr avant d'être tué par SIGPIPE . Je pense que votre solution n'empêche pas le processus producteur ( typeici) de réagir à l'écriture échouée (en raison de la fermeture du tuyau), n'est-ce pas?
Piotr Dobrogost