Pourquoi les variables non-environnement sont-elles transmises au sous-shell invoqué par la substitution de command?

Bash manuel dit:

La substitution de command, les commands groupées avec des parenthèses et les commands asynchronouss sont appelées dans un environnement sous-shell qui est un doublon de l'environnement shell, excepté que les traps capturés par le shell sont réinitialisés aux valeurs héritées de son parent lors de l'invocation.

Dans cet exemple, b n'est pas une variable d'environnement, donc b n'existe pas dans le sous-shell créé par substitution de command. Alors pourquoi c est-il assigné la valeur de b par substitution de command? Est-ce parce que l'expansion des parameters se produit pour $b dans le process shell avant de créer un sous-shell pour exécuter l' echo 1 ?

 $ b=1 $ c=$(echo $b) $ echo $c 1 

Non, le sous-shell a été créé en premier.

Un environnement d'exécution de shell contient des parameters shell définis par des affectations de variables et des variables d'environnement. Un environnement sous-shell a été créé en dupliquant l'environnement du shell, il contient donc toutes les variables de l'environnement shell actuel.

Voir l'exemple:

 $ b=1 $ c=$(b=2; echo "$b") $ echo "$c" 2 

La sortie est 2 au lieu de 1 .


Un environnement sous-shell créé par substitution de command est différent avec un environnement de shell créé en appelant l'exécutable du shell.

Lorsque vous appelez le shell comme:

 $ bash -c : 

le shell courant utilisé execve () pour créer un nouveau process shell, quelque chose comme:

 execve("/bin/bash", ["bash", "-c", ":"], [/* 64 vars */]) = 0 

le dernier argument passé à execve contient toutes les variables d'environnement.

C'est pourquoi vous devez exporter les variables pour les pousser vers les variables d'environnement qui seront incluses dans les commands exécutées ultérieurement:

 $ a=; export a $ strace -e execve bash -c : execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0 +++ exited with 0 +++ 

Notez que les variables d'environnement passent de 64 à 65. Et les variables qui ne sont pas exscopes ne seront pas transmises au nouvel environnement shell:

 $ a=; b=; export a $ strace -e execve bash -c : execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0 +++ exited with 0 +++ 

Notez que les variables d'environnement sont toujours 65.


Dans la substitution de commands, le shell a utilisé fork () pour créer un nouveau process shell, qui a simplement copié l'environnement shell actuel – qui contient à la fois les variables set et les variables d'environnement.

Oui b n'est pas une variable d'environnement.
Mais: Oui, b existe dans le sous-shell créé par la substitution de command:

 $ b=11; c="$(echo $b)"; echo "$c" ### b exists in subshell. 11 $ b=11; c="$(b=33; echo $b)"; echo "$c" ### $b is not replaced before 33 ### the subshell is executed. 

Ce qui ne reçoit pas les variables est un "process enfant complet" :

 $ b=11; bash -c 'echo "<$b>"' ### b does not exist. <> $ b=11 bash -c 'echo "<$b>"' ### environment b. <11> 

Sauf qu'un process peut recevoir des variables dans l'environnement, bien sûr.


Dernière ligne à Wooledge SubShell :

Dans le sous-shell, la variable de shell régulière a est visible; mais parce qu'il n'est pas exporté, le process enfant complet ne le voit pas.

La substitution de command provoque un appel normal vers l'interpréteur shell et cet interpréteur exécutera la command echo dans un sub-process .

Le sub-process est nécessaire car le shell doit établir un pipe vers la command echo afin de pouvoir lire les résultats.

Le sub-process qui exécute la command echo est créé par fork() et crée des copys de toutes les variables du process shell principal. C'est pourquoi $b est accessible par la command echo .