Dans la terminologie POSIX, un environnement de sous-shell est lié à la notion d' environnement d'exécution Shell .
Un environnement de sous-shell est un environnement d'exécution de shell distinct créé en tant que doublon de l'environnement parent. Cet environnement d'exécution comprend des choses comme les fichiers ouverts, umask, le répertoire de travail, les variables / fonctions / alias du shell ...
Les modifications apportées à cet environnement de sous-shell n'affectent pas l'environnement parent.
Traditionnellement dans le shell Bourne ou ksh88 sur lequel la spécification POSIX est basée, cela se faisait en forçant un processus enfant.
Les domaines dans lesquels POSIX requiert ou autorise l'exécution de la commande dans un environnement de sous-shell sont ceux où ksh88 a traditionnellement bifurqué un processus shell enfant.
Cela ne force cependant pas les implémentations à utiliser un processus enfant pour cela.
Un shell peut choisir d'implémenter cet environnement d'exécution séparé comme bon lui semble.
Par exemple, ksh93 le fait en enregistrant les attributs de l'environnement d'exécution parent et en les restaurant à la fin de l'environnement de sous-shell dans des contextes où le forking peut être évité (comme une optimisation car le forking est assez cher sur la plupart des systèmes).
Par exemple, dans:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX a besoin cd /foo
de s'exécuter dans un environnement séparé et de produire quelque chose comme:
/foo
/bar
/foo
Il ne nécessite pas qu'il s'exécute dans un processus distinct. Par exemple, si stdout devient un tube cassé, pwd
exécuté dans l'environnement de sous-shell pourrait très bien envoyer le SIGPIPE au seul et unique processus shell.
La plupart des shells, y compris bash
, l'implémenteront en évaluant le code à l'intérieur (...)
d'un processus enfant (pendant que le processus parent attend sa fin), mais ksh93 le fera à la place lors de l'exécution du code à l'intérieur (...)
, le tout dans le même processus:
- rappelez-vous que c'est dans un environnement de sous-shell.
- sur
cd
, enregistrez le répertoire de travail précédent (généralement sur un descripteur de fichier ouvert avec O_CLOEXEC), enregistrez la valeur des variables OLDPWD, PWD et tout ce qui cd
pourrait modifier, puis effectuez lachdir("/bar")
- au retour du sous-shell, le répertoire de travail actuel est restauré (avec un
fchdir()
sur ce fd enregistré), et tout ce que le sous-shell peut avoir modifié.
Il existe des contextes où un processus enfant ne peut pas être évité. ksh93 ne débouche pas:
var=$(subshell)
(subshell)
Mais en
{ subshell; } &
{ subshell; } | other command
C'est-à-dire les cas où les choses doivent s'exécuter dans des processus séparés pour pouvoir s'exécuter simultanément.
Les optimisations de ksh93 vont plus loin. Par exemple, en
var=$(pwd)
la plupart des shells bifurquent un processus, demandent à l'enfant d'exécuter la pwd
commande avec sa sortie standard redirigée vers un canal, pwd
écrivent le répertoire de travail actuel dans ce canal et le processus parent lit le résultat à l'autre extrémité du canal, ksh93
virtualise tout cela par aucun des deux nécessitant la fourche ni le tuyau. Une fourche et un tuyau ne seront utilisés que pour les commandes non intégrées.
Notez qu'il existe des contextes autres que des sous-shell pour lesquels les shells forkent un processus enfant. Par exemple, pour exécuter une commande qui est stockée dans un exécutable séparé (et qui n'est pas un script destiné au même interpréteur de shell), un shell devrait bifurquer un processus pour exécuter cette commande, sinon il ne le serait pas capable d'exécuter plus de commandes après le retour de cette commande.
Dans:
/bin/echo "$((n += 1))"
Ce n'est pas un sous-shell, la commande sera évaluée dans l'environnement d'exécution du shell actuel, la n
variable de l'environnement d'exécution du shell actuel sera incrémentée, mais le shell forkera un processus enfant pour exécuter cette /bin/echo
commande avec l'expansion de l' $((n += 1))
argument as .
De nombreux shells implémentent une optimisation en ce sens qu'ils ne forcent pas un processus enfant pour exécuter cette commande externe s'il s'agit de la dernière commande d'un script ou d'un sous-shell (pour les sous-shells qui sont implémentés en tant que processus enfants). ( bash
mais seulement si cette commande est la seule commande du sous-shell).
Cela signifie que, avec ces shells, si la dernière commande dans le sous-shell est une commande externe, le sous-shell ne provoque pas la génération d'un processus supplémentaire. Si vous comparez:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
avec
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
il y aura le même nombre de processus créés, seulement dans le deuxième cas, le deuxième fork est fait plus tôt pour que le a=2
soit exécuté dans un environnement de sous-shell.