Le Learning Bash Book mentionne qu'un sous-shell héritera uniquement des variables d'environnement et des descripteurs de fichiers, etc., et qu'il n'héritera pas des variables qui ne sont pas exportées:
$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var
$
Comme je sais, le shell créera deux sous-coquilles pour ()
et pour ./file
, mais pourquoi dans le ()
cas la sous-coquille identifie-t-elle la var
variable bien qu'elle ne soit pas exportée et dans le ./file
cas où elle ne l'a pas identifiée?
# Strace for ()
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631
J'ai essayé d'utiliser strace
pour comprendre comment cela se produit et, de façon surprenante, j'ai trouvé que bash utilisera les mêmes arguments pour l'appel système clone, ce qui signifie que le processus forké ()
et ./file
devrait avoir le même espace d'adressage de processus du parent, alors pourquoi dans le ()
cas, la variable est visible pour le sous-shell et la même chose ne se produit pas pour le ./file
cas, bien que les mêmes arguments soient basés sur l'appel système du clone?
Réponses:
Le Learning Bash Book est faux. Les sous-coques héritent de toutes les variables. Même
$$
(le PID de la coque d'origine) est conservé. La raison en est que pour un sous-shell, le shell ne fait que forger et n'exécute pas un nouveau shell (au contraire, lorsque vous tapez./file
, une nouvelle commande est exécutée, par exemple un nouveau shell; dans la sortie strace, regardezexecve
et similaire) . Donc, en gros, c'est juste une copie (avec quelques différences documentées).Remarque: ce n'est pas spécifique à bash; cela est vrai pour n'importe quel shell.
la source
-f
option de suivi desstrace
enfants? C'est nécessaire pour trouver les exécutifs.Soit vous, soit le livre confondez un sous-shell avec un sous-processus qui est un shell.
Certaines constructions de shell entraînent le shell à forger un processus enfant. Sous Linux,
fork
est un cas particulier de l'clone
appel système plus général , que vous avez observé dans lestrace
journal. L'enfant exécute une partie du script shell. Le processus enfant est appelé sous - shell . La construction la plus directe estcommand1 &
:command1
s'exécute dans un sous-shell et les commandes suivantes s'exécutent dans le shell parent. D'autres constructions qui créent un sous-shell incluent la substitution de commandes$(command2)
et les canauxcommand3 | command4
(command3
s'exécute dans un sous-shell,command4
s'exécute dans un sous-shell dans la plupart des shells mais pas dans ksh ou zsh).Un sous-shell est une copie du processus parent, il a donc non seulement les mêmes variables d'environnement, mais aussi toutes les mêmes définitions internes: variables (y compris
$$
, l'ID de processus du processus shell d'origine), fonctions, alias, options, etc. Avant d'exécuter le code dans le sous-shell, bash définit la variableBASHPID
sur l'ID de processus du processus enfant.Lorsque vous exécutez
./file
, cela exécute une commande externe. Tout d'abord, le shell lance un processus enfant; puis ce processus enfant exécute (avec l'execve
appel système) le fichier exécutable./file
. Un processus enfant hérite des attributs de processus de ses parents: environnement, répertoire courant, etc. Les aspects internes de l'application sont perdus dans l'execve
appel: les variables, fonctions, etc. non exportées sont des notions bash que le noyau ne connaît pas, et ils sont perdus lorsque bash exécute un autre programme. Même si cet autre programme se trouve être un script bash, il est exécuté par une nouvelle instance de bash qui ne sait pas ou ne se soucie pas que son processus parent se trouve être également une instance de bash. Ainsi, une variable shell (variable non exportée) ne survit pasexecve
.la source