GNU/Linux >> Tutoriels Linux >  >> Linux

La sortie de substitution de processus n'est pas dans l'ordre ?

Le

echo one; echo two > >(cat); echo three; 

la commande donne une sortie inattendue.

J'ai lu ceci :Comment la substitution de processus est-elle implémentée dans bash ? et de nombreux autres articles sur la substitution de processus sur Internet, mais je ne comprends pas pourquoi il se comporte de cette façon.

Résultat attendu :

one
two
three

Sortie réelle :

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

De plus, ces deux commandes devraient être équivalentes de mon point de vue, mais ce n'est pas le cas :

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

Pourquoi je pense qu'ils devraient être les mêmes? Parce que les deux connectent le seq sortie vers le cat entrée via le canal anonyme - Wikipedia, substitution de processus.

Question : Pourquoi se comporte-t-il ainsi ? Où est mon erreur ? La réponse complète est souhaitée (avec explication de la façon dont le bash le fait sous le capot).

Réponse acceptée :

Oui, en bash comme dans ksh (d'où vient la fonctionnalité), les processus à l'intérieur de la substitution de processus ne sont pas attendus (avant d'exécuter la commande suivante dans le script).

pour un <(...) un, c'est généralement bien comme dans :

cmd1 <(cmd2)

le shell attendra cmd1 et cmd1 attendra généralement cmd2 en raison de sa lecture jusqu'à la fin du fichier sur le canal qui est remplacé, et cette fin de fichier se produit généralement lorsque cmd2 meurt. C'est la même raison pour laquelle plusieurs shells (pas bash ) ne vous embêtez pas à attendre cmd2 dans cmd2 | cmd1 .

Pour cmd1 >(cmd2) , cependant, ce n'est généralement pas le cas, car il s'agit plutôt de cmd2 qui attend généralement cmd1 il sortira donc généralement après.

C'est corrigé dans zsh qui attend cmd2 là (mais pas si vous l'écrivez comme cmd1 > >(cmd2) et cmd1 n'est pas intégré, utilisez {cmd1} > >(cmd2) à la place comme documenté).

ksh n'attend pas par défaut, mais vous permet de l'attendre avec le wait intégré (il rend également le pid disponible dans $! , bien que cela n'aide pas si vous faites cmd1 >(cmd2) >(cmd3) )

rc (avec le cmd1 >{cmd2} syntaxe), identique à ksh sauf que vous pouvez obtenir les pids de tous les processus d'arrière-plan avec $apids .

es (également avec cmd1 >{cmd2} ) attend cmd2 comme dans zsh , et attend également cmd2 dans <{cmd2} traiter les redirections.

bash fait le pid de cmd2 (ou plus exactement du sous-shell car il exécute cmd2 dans un processus enfant de ce sous-shell même s'il s'agit de la dernière commande) disponible dans $! , mais ne vous laisse pas attendre.

Si vous devez utiliser bash , vous pouvez contourner le problème en utilisant une commande qui attendra les deux commandes avec :

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

Cela fait à la fois cmd1 et cmd2 ont leur fd 3 ouvert sur un tuyau. cat attendra la fin du fichier à l'autre extrémité, donc ne sortira généralement que lorsque les deux cmd1 et cmd2 sont mortes. Et le shell attendra ce cat commande. Vous pouvez voir cela comme un filet pour attraper la fin de tous les processus d'arrière-plan (vous pouvez l'utiliser pour d'autres choses démarrées en arrière-plan comme avec & , des coprocs ou même des commandes qui s'arrière-plan à condition qu'ils ne ferment pas tous leurs descripteurs de fichiers comme le font généralement les démons).

En relation :Qu'est-ce qui fait que les fichiers perdent les autorisations ?

Notez que grâce à ce processus de sous-shell gaspillé mentionné ci-dessus, cela fonctionne même si cmd2 ferme son fd 3 (les commandes ne le font généralement pas, mais certaines comme sudo ou ssh faire). Les futures versions de bash peut éventuellement y faire l'optimisation comme dans d'autres shells. Ensuite, vous auriez besoin de quelque chose comme :

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

Pour s'assurer qu'il y a encore un processus shell supplémentaire avec ce fd 3 ouvert en attente de ce sudo commande.

Notez que cat ne lira rien (puisque les processus n'écrivent pas sur leur fd 3). C'est juste là pour la synchronisation. Il ne fera qu'un seul read() appel système qui retournera sans rien à la fin.

Vous pouvez en fait éviter d'exécuter cat en utilisant une substitution de commande pour faire la synchronisation du tube :

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

Cette fois, c'est le shell au lieu de cat qui lit depuis le tube dont l'autre extrémité est ouverte sur fd 3 de cmd1 et cmd2 . Nous utilisons une affectation de variable afin que le statut de sortie de cmd1 est disponible en $? .

Ou vous pouvez effectuer la substitution de processus à la main, puis vous pouvez même utiliser le sh de votre système car cela deviendrait la syntaxe standard du shell :

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

mais notez comme indiqué précédemment que tous les sh les implémentations attendraient cmd1 après cmd2 a fini (même si c'est mieux que l'inverse). Cette fois, $? contient le statut de sortie de cmd2; bien que bash et zsh faire cmd1 Le statut de sortie de est disponible dans ${PIPESTATUS[0]} et $pipestatus[1] respectivement (voir aussi le pipefail option dans quelques shells donc $? peut signaler la défaillance de composants de tuyauterie autres que le dernier)

Notez que yash a des problèmes similaires avec son processus de redirection fonctionnalité. cmd1 >(cmd2) s'écrirait cmd1 /dev/fd/3 3>(cmd2) là. Mais cmd2 n'est pas attendu et vous ne pouvez pas utiliser wait de l'attendre non plus et son pid n'est pas rendu disponible dans le $! variables non plus. Vous utiliseriez les mêmes solutions de contournement que pour bash .


Linux
  1. Nouveau processus parent lorsque le processus parent meurt ?

  2. Qu'advient-il de la sortie d'un processus qui a été désavoué et a perdu son terminal ?

  3. Linux :savoir quel processus utilise toute la RAM ?

  4. Savoir si le système d'exploitation s'exécute dans un environnement virtuel

  5. Création de fichier temporaire vs substitution de processus vs expansion de variable ?

Que signifie la sortie de Ps ?

La méthode portable (posix) pour réaliser la substitution de processus ?

SIGTERM vs SIGKILL :Quelle est la différence ?

Substitution de processus :une méthode peu courante mais avancée pour la redirection d'entrée/sortie sous Linux

Exécuter le processus avec une sortie en temps réel en PHP

Obtenez les 4 derniers caractères de la sortie de la sortie standard