Comment bash traite ">> ()"

9

En expérimentant la redirection de sortie et la substitution de processus, je suis tombé sur la commande suivante et sa sortie résultante:

    me @ elem: ~ $ echo foo>> (chat); barre d'écho
    bar
    moi @ elem: ~ $ foo

(Oui, cette nouvelle ligne vide à la fin est intentionnelle.)

Alors bash echo's bar, imprime mon invite habituelle, echo's foo, echo's a newline, et laisse mon curseur là. Si j'appuie de nouveau sur Entrée, il affichera mon invite sur une nouvelle ligne et laissera le curseur le suivre (comme prévu lorsque quelqu'un frappe Entrée sur une ligne de commande vide).

Je m'attendais à ce qu'il écrive foo dans un descripteur de fichier, cat le lit et foo echo, la deuxième barre d'écho echo, puis reviens à l'invite de commande. Mais ce n'est clairement pas le cas.

Quelqu'un pourrait-il expliquer ce qui se passe?

Stanley Yu
la source
Je voudrais suggérer de chercher les réponses à ce sujet: unix.stackexchange.com/questions/182800/… Il explique la double redirection et expliquerait pourquoi vous vous retrouvez avec des résultats différents. Muru et Michael Hormers.
Pas le
Pour une question expliquant explicitement pourquoi la sortie de la substitution de processus apparaît après l'invite (et dans certaines circonstances n'apparaît pas du tout) dans certaines versions du shell Bourne Again, voir unix.stackexchange.com/questions/471987 .
JdeBP

Réponses:

9

Vous pouvez voir foos'afficher avant bar, après barou même après l'invite, selon le moment. Ajoutez un peu de retard pour obtenir un timing cohérent:

$ echo foo > >(sleep 1; cat); echo bar; sleep 2 
bar
foo
$

barapparaît immédiatement, puis fooaprès une seconde, puis l'invite suivante après une autre seconde.

Ce qui se passe, c'est que bash exécute les substitutions de processus en arrière-plan.

  1. Le processus bash principal démarre un sous-shell à exécuter sleep 1; catet configure un canal vers celui-ci.
  2. Le processus bash principal s'exécute echo foo. Comme cela ne remplit pas le tampon du tube, la echocommande se termine sans blocage.
  3. Le processus bash principal s'exécute echo bar.
  4. Le processus bash principal lance la commande sleep 2.
  5. Pendant ce temps, le sous-shell lance la commande sleep 1.
  6. Après environ 1 seconde, sleep 1retourne dans le sous-processus. Le sous-processus s'exécute cat.
  7. cat copie son entrée dans sa sortie (qui s'affiche à l'écran) et revient.
  8. Le sous-shell a terminé son travail, il sort.
  9. Après une autre seconde, sleep 2revient. Le processus shell principal termine son exécution et vous obtenez la prochaine invite.
Gilles 'SO- arrête d'être méchant'
la source
3

Vous n'avez pas besoin de substitution de processus pour obtenir cet effet. Essaye ça:

( echo foo | cat & )

Vous obtiendrez à peu près le même résultat (sans le bar; je laisserai cela comme un exercice) et pour la même raison. Dans les deux cas, catdémarre comme tâche d'arrière-plan. Dans ma ligne ici, c'est explicite. Dans le cas de la substitution de processus, c'est aussi explicite - c'est un processus distinct attaché à un descripteur de fichier de nom - mais ce n'est peut-être pas aussi évident.

Le processus enfant ne se termine pas nécessairement avant d' bashimprimer l'invite suivante. Et comme sa sortie est mise en mémoire tampon, elle ne produit rien tant qu'elle n'est pas terminée. À ce stade, bash vient d'imprimer $sur stderr, et maintenant le processus d'arrière-plan se termine après l'impression fooet une nouvelle ligne.

rici
la source