D'après ce que j'ai lu, mettre une commande entre parenthèses devrait l'exécuter dans un sous-shell, similaire à l'exécution d'un script. Si c'est vrai, comment voit-il la variable x si x n'est pas exporté ?
x=1
Exécution de (echo $x)
sur la ligne de commande donne 1
Exécution de echo $x
dans un script ne donne rien, comme prévu
Réponse acceptée :
Un sous-shell commence comme une copie presque identique du processus shell d'origine. Sous le capot, le shell appelle le fork
appel système, qui crée un nouveau processus dont le code et la mémoire sont des copies. Lorsque le sous-shell est créé, il y a très peu de différences entre lui et son parent. En particulier, ils ont les mêmes variables. Même le $$
La variable spéciale conserve la même valeur dans les sous-shells :c'est l'ID de processus du shell d'origine. De même $PPID
est le PID du parent du shell d'origine.
Quelques shells modifient quelques variables dans le sous-shell. Bash définit BASHPID
au PID du processus shell, qui change dans les sous-shells. Bash, zsh et mksh s'arrangent pour $RANDOM
pour produire des valeurs différentes dans le parent et dans le sous-shell. Mais en dehors des cas spéciaux intégrés comme ceux-ci, toutes les variables ont la même valeur dans le sous-shell que dans le shell d'origine, le même statut d'exportation, le même statut de lecture seule, etc. Toutes les définitions de fonction, les définitions d'alias, les options du shell et d'autres paramètres sont également hérités.
Un sous-shell créé par (…)
a les mêmes descripteurs de fichier que son créateur. D'autres moyens de créer des sous-interpréteurs modifient certains descripteurs de fichiers avant d'exécuter le code utilisateur ; par exemple, le côté gauche d'un tuyau s'exécute dans un sous-shell avec une sortie standard connectée au tuyau. Le sous-shell démarre également avec le même répertoire courant, le même masque de signal, etc. L'une des rares exceptions est que les sous-shells n'héritent pas des traps personnalisés :signaux ignorés (trap '' SIGNAL
) restent ignorés dans le sous-shell, mais d'autres traps (trap CODE
SIGNAL ) sont réinitialisés à l'action par défaut.
Un sous-shell est donc différent de l'exécution d'un script. Un script est un programme séparé. Ce programme séparé pourrait par coïncidence être aussi un script qui est exécuté par le même interpréteur que le parent, mais cette coïncidence ne donne pas au programme séparé une visibilité particulière sur les données internes du parent. Les variables non exportées sont des données internes, donc lorsque l'interpréteur du script shell enfant est exécuté, il ne voit pas ces variables. Les variables exportées, c'est-à-dire les variables d'environnement, sont transmises aux programmes exécutés.
Ainsi :
x=1
(echo $x)
imprime 1
car le sous-shell est une réplique du shell qui l'a engendré.
x=1
sh -c 'echo $x'
arrive à exécuter un shell en tant que processus enfant d'un shell, mais le x
sur la deuxième ligne n'a plus de rapport avec le x
sur la deuxième ligne que dans
x=1
perl -le 'print $x'
ou
x=1
python -c 'print x'
Une exception est le ksh93
shell où le forking est optimisé et la plupart de ses effets secondaires sont émulés.
Sémantiquement, ce sont des copies. Du point de vue de la mise en œuvre, il y a beaucoup de partage en cours.
Pour le côté droit, cela dépend du shell.
Si vous testez ceci, notez que des choses comme $(trap)
peut signaler les pièges de la coque d'origine. Notez également que de nombreux shells ont des bogues dans les cas de coin impliquant des pièges. Par exemple, ninjalj note qu'à partir de bash 4.3, bash -x -c 'trap "echo ERR at $BASH_SUBSHELL $BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'
exécute le ERR
trap du sous-shell imbriqué dans le cas "deux sous-shells", mais pas le ERR
trap du sous-shell intermédiaire — set -E
l'option doit propager le ERR
trap à tous les sous-shells mais le sous-shell intermédiaire est optimisé et n'est donc pas là pour exécuter son ERR
piège.