Un programme suivant dans un pipeline peut-il voir le code de sortie du programme précédent?

8

Je voudrais créer un pipeline de scripts Bash comme celui-ci

prog1 | prog2

de telle sorte que prog2 puisse voir le code de sortie de prog1 et agir différemment en fonction de ces informations.

Est-ce possible?

dan
la source
Pourriez-vous élaborer la loi différemment dans le cadre de votre question?
devnull
Vous avez un contrôle limité sur la distance prog2a progressé lorsque les prog1sorties, en raison de la mise en mémoire tampon interne utilisé pour mettre en œuvre la conduite et comment prog1et prog2sont prévues.
chepner
Regardez également [Dans quel ordre s'exécutent les commandes canalisées?] (Unix.stackexchange.com/q/37508)
DK Bose

Réponses:

4

La réponse générale est non. Il est possible prog2de quitter avant prog1même de commencer (cela ne peut évidemment pas se produire s'il prog2lit réellement une entrée, ce que vous attendez qu'il fasse si vous l'utilisez dans un pipeline). Il est certainement possible prog2de sortir avant prog1; cela se produit par exemple quand prog2un programme de recherche prog1se termine dès qu'il trouve une correspondance, auquel cas il se peut qu'il n'ait pas encore fini de produire toutes les données.

Il n'y a aucun moyen direct prog2de récupérer l'état de sortie de prog1ou même de savoir qu'il prog1est sorti. Tout ce que l' prog2on peut savoir, c'est qu'il prog1a fermé son extrémité du tuyau, ce qu'il peut faire sans mourir.

Si vous souhaitez obtenir l'état de sortie de prog1from prog2, il existe deux méthodes courantes: vous pouvez l'écrire dans un fichier ou l'envoyer via le canal. L'envoi de l'état de sortie comme dernière ligne des données canalisées est une possibilité. Vous devez vous assurer de ne pas traiter la dernière ligne jusqu'à ce que vous sachiez que c'est la dernière ligne, c'est-à-dire jusqu'à ce que vous ayez essayé de lire la ligne suivante.

{ prog1; echo $?; } | 

Voici un exemple où le côté droit est un filtre de texte qui colore chaque ligne contenant le mot «erreur» en rouge. Si le côté gauche échoue, le côté droit sort avec le même état.

{ prog1; echo $?; } | awk '
    NR != 1 {
        if (line ~ /[Ee][Rr][Rr][Oo][Rr]/) print "\033[31m" line "\033[0m";
        else print line;
    }
    {line = $0}
    END {exit($0)}
'
Gilles 'SO- arrête d'être méchant'
la source
J'essayais { command; echo ${PIPESTATUS[@]}; } | sort | ...pour que le statut de sortie vienne en premier dans le flux. C'est très intéressant!
@ illuminÉ Cela ne fonctionnerait que si ${PIPESTATUS[@]}est trié avant toute autre chose dans la sortie de command. Si commandimprime un tas de nombres, ou s'il peut imprimer du texte arbitraire, vous avez des problèmes: vous ne pourrez pas distinguer sa sortie de la ligne d'état.
Gilles 'SO- arrête d'être méchant'
Merci, en effet, il ne réussit à trier un état de réussite que si la commande ne contient pas 0 dans sa sortie lol. Tgif / s.
2

Bien que vous puissiez le faire dans certains cas particuliers (voir les autres réponses), vous ne le pouvez pas dans tous les cas. Certains programmes de filtrage continueront tout simplement, tandis que d'autres conserveront toutes les sorties, les libéreront en une seule fois, puis quitteront.

Pour un exemple de programme "continuez tout simplement", grepsera serveur, comme le ferait tail -f /var/log/some_log_file. L'utilisation sortdans un pipeline provoque un "décrochage", de même que la sortcollecte de l'entrée jusqu'à la fermeture du tuyau devant lui. L'utilisation xargsajoute une complication supplémentaire: les programmes sont-ils démarrés par xargs(cela pourrait démarrer de nombreuses instances) une partie du pipeline ou non?

Bruce Ediger
la source
-1 parce que vous avez voté pour rationaliser une réponse incorrecte.
ctrl-alt-delor
@richard Huh? La réponse de Bruce est correcte (bien que le deuxième paragraphe soit un peu déroutant).
Gilles 'SO- arrête d'être méchant'
1

La réponse: pas directement.

@terdon a illustré que le code de sortie de la commande précédente dans le canal doit être envoyé en tant que paramètre explicite à la commande suivante.

N'oubliez pas que le canal est simplement un mappage du STDOUT de la commande précédente au STDIN de la commande suivante; les codes de sortie ne sont pas sortis vers STDOUT (ou STDERR).

pepoluan
la source
1
-1 parce que vous avez voté pour avoir cité une réponse incorrecte, sans en dire beaucoup plus.
ctrl-alt-delor
@richard assez juste ... j'aurais dû vérifier deux fois ... c'est ce qui s'est passé si je me force à répondre à une question quand je suis très fatigué ...
pepoluan
1

Tous les processus, en cours, sont démarrés avant toute sortie. Par conséquent, il prog2pourrait être nécessaire d'obtenir ces informations après le démarrage, il devrait également suspendre le traitement jusqu'à la fin prog1, ce qui pourrait bloquer le tuyau. Il semble y avoir des problèmes fondamentaux à faire ce que vous demandez, pas des limitations du système d'exploitation.

Vous devrez probablement considérer un fichier temporaire ou mettre le résultat dans une variable.

Exemple pour une petite quantité de données, en utilisant une variable.

tmp=$(prog1)
if test "z$PIPESTATUS" == "z0"
then
   
else
   
fi
ctrl-alt-delor
la source
Il y a une lacune dans votre raisonnement. prog2est démarré avant la prog1fin en général, mais il pourrait y avoir un moyen de recevoir l'état de sortie prog1pendant qu'il est en cours d'exécution.
Gilles 'SO- arrête d'être méchant'
0

Pour terminer la réponse de Gilles ,

(prog1; echo $? > /tmp/prog1.status) | prog2

est une approche.  prog2pourrait soit

  • lire l'entrée standard jusqu'à la fin, puis lire /tmp/prog1.status, ou
  • vérifier l'existence de /tmp/prog1.statuspériodiquement lors de la lecture de l'entrée standard.
Scott
la source