Tuyau semi-asynchrone


11

Supposons que j'ai le tuyau suivant:

a | b | c | d

Comment puis-je attendre la fin de c(ou b) dans shou bash? Cela signifie que le script dpeut démarrer à tout moment (et n'a pas besoin d'être attendu) mais nécessite une sortie complète de cpour fonctionner correctement.

Le cas d'utilisation est un difftoolpour gitqui compare les images. Il est appelé par gitet doit traiter son entrée (la a | b | cpartie) et afficher les résultats de la comparaison (la dpartie). L'appelant supprimera l'entrée requise pour aet b. Cela signifie qu'avant de revenir du script, le processus c(ou b) doit se terminer. D'un autre côté, je ne peux pas attendre dcar cela signifie que j'attends la saisie de l'utilisateur.

Je sais que je peux écrire les résultats de cdans un fichier temporaire, ou peut-être utiliser un FIFO dans bash. (Je ne sais pas si le FIFO va aider, cependant.) Est-il possible d'y parvenir sans fichiers temporaires sh?

ÉDITER

Il serait peut-être suffisant que je puisse trouver l'ID de processus du c(ou b) processus de manière fiable. Ensuite, le tube entier pourrait être démarré de manière asynchrone et je pouvais attendre un ID de processus. Quelque chose dans le sens de

wait $(a | b | { c & [print PID of c] ; } | d)

MODIFIER ^ 2

J'ai trouvé une solution, les commentaires (ou encore meilleures solutions) sont les bienvenus.


Vous voulez dire que vous voulez dcommencer cla sortie du traitement uniquement après la cfin? Vous ne voulez dpas commencer à traiter chaque ligne de sortie au fur et à mesure?
terdon

@terdon: Non, dest libre de commencer quand bon lui semble, mais cdoit se terminer avant que je puisse continuer.
krlmlr

Cela semble contradictoire, si vous dpouvez commencer quand vous le souhaitez, qu'attendez-vous exactement?
terdon

@terdon: développé pour montrer le cas d'utilisation.
krlmlr

Si dn'utilise pas la sortie de calors il ne semble pas logique de faire dpartie du pipeline. Mais si dutilise l'entrée, il dfaut travailler un moment sur son entrée après l'avoir lue pour que votre approche fasse la différence.
Hauke ​​Laging du

Réponses:


6
a | b | { c; [notify];} | d

La notification peut être effectuée par exemple par un signal à un PID qui a été passé dans une variable d'environnement ( kill -USR1 $EXTPID) ou en créant un fichier ( touch /path/to/file).

Une autre idée:

Vous exécutez le processus suivant (celui que vous souhaitez démarrer) à partir du pipeline:

a | b | { c; exec >&-; nextprocess;} | d

ou

a | b | { c; exec >&-; nextprocess &} | d

Merci. Mais ensuite, la création d'un fichier temporaire pour la sortie intermédiaire est plus détaillée et plus facile à analyser (pour un humain). De plus, comment puis-je éviter les conditions de course avec le signal? ... J'espérais une solution plus propre, mais si c'est la voie à suivre, alors tant pis.
krlmlr le

@krlmlr Quelle condition de concurrence?
Hauke ​​Laging du

notifypourrait être exécuté avant de configurer le piège de signal. Comment puis-je m'assurer de ne pas manquer ce signal?
krlmlr

@krlmlr Vous arrêtez le script qui appelle le pipeline jusqu'à ce qu'il reçoive lui-même un signal qui est envoyé après que le trapa été défini.
Hauke ​​Laging du

Votre montage: le tuyau est appelé dans une boucle, sur laquelle je n'ai aucun contrôle. Malheureusement, { c; bg; }ou { c; exit 0; }ne semble pas fonctionner.
krlmlr

5

Si je comprends bien votre question, cela devrait fonctionner:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(l'astuce fd 3 est parce que certains (la plupart) des shells redirigent stdin vers / dev / null avec &).



3

C'est ce que j'ai trouvé par essais et erreurs, avec l'aide de la contribution de Hauke:

a | b | { c; kill -PIPE $$; } | d

De manière équivalente:

a | b | ( c; kill -PIPE $$; ) | d

(Ce dernier est plus explicite, car {}s'exécutera de toute façon dans un sous-shell si à l'intérieur d'un tuyau.)

D'autres signaux (y compris QUIT, TERMet USR1) le travail aussi, mais dans ce cas , la description du signal est affiché sur le terminal.

Je me demande si c'est l'intention originale du PIPEsignal. Selon le manuel :

SIGPIPE: Le PIPEsignal est envoyé à un processus lorsqu'il tente d'écrire sur un tuyau sans qu'un processus soit connecté à l'autre extrémité.

Cela signifie que lorsque j'envoie artificiellement un signal de tuyau au sous-shell, il se termine silencieusement, laissant le consommateur final ( d) seul.

Cela fonctionne à la fois dans shet bash.


0

Vous pouvez utiliser le spongeprogramme du package "moreutils":

a | b | c | sponge | d

l'éponge sera pour la fin de cla sortie avant de la canaliser d. J'espère que c'est ce que tu voulais.


0

Vous pouvez simplement faire:

a | b | c | (d ; cat > /dev/null)

Ainsi, une fois dterminé, le catabsorbera le reste de cla production jusqu'à ce qu'il se termine.

D'accord. Après les commentaires, je pense que la réponse est de commencer directement den arrière-plan.

Faire:

a | b | c | (d &)

ou utilisez la solution de Stéphane Chazelas en cas de problème de dlecture depuis stdin .


Je crains que mon cas d'utilisation soit plutôt l'inverse: ctermine plus tôt que d, et je dois attendre jusqu'à la cfin, mais je m'en fiche d.
krlmlr

Désolé, je ne comprends pas. Que voulez-vous faire une fois cterminé et den cours d'exécution? Tuer d?
angus

dpossède une interface graphique et peut s'exécuter jusqu'à ce que l'utilisateur la ferme.
krlmlr

OK ... mais cela se produit déjà. Quand a, bet cfinissant leur travail, ils ferment la sortie standard et sortent; et ne dreste en cours d'exécution. Voulez-vous envoyer dà l'arrière-plan une fois cterminé? Est-ce que c'est ça?
angus

Oui, ddoit être envoyé à l'arrière-plan une fois cterminé.
krlmlr le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.