Una subshell inizia come una copia quasi identica del processo di shell originale. Sotto il cofano, la shell chiama la fork
chiamata di sistema 1 , che crea un nuovo processo il cui codice e memoria sono copie 2 . Quando viene creata la subshell, ci sono pochissime differenze tra essa e il suo genitore. In particolare, hanno le stesse variabili. Anche la $$
variabile speciale mantiene lo stesso valore nei subshells: è l'ID del processo della shell originale. Allo stesso modo $PPID
è il PID del genitore della shell originale.
Alcune shell cambiano alcune variabili nella subshell. Bash imposta BASHPID
il PID del processo shell, che cambia in subshells. Bash, zsh e mksh provvedono $RANDOM
a produrre valori diversi nel genitore e nella subshell. Ma a parte casi speciali incorporati come questi, tutte le variabili hanno lo stesso valore nella subshell come nella shell originale, lo stesso stato di esportazione, lo stesso stato di sola lettura, ecc. Tutte le definizioni di funzione, definizioni di alias, opzioni di shell e anche altre impostazioni vengono ereditate.
Una subshell creata da (…)
ha gli stessi descrittori di file del suo creatore. Alcuni altri mezzi per creare subshells modificano alcuni descrittori di file prima di eseguire il codice utente; ad esempio, il lato sinistro di una pipe viene eseguito in una subshell 3 con output standard collegato alla pipe. La subshell inizia anche con la stessa directory corrente, la stessa maschera di segnale, ecc. Una delle poche eccezioni è che le subshell non ereditano le trap personalizzate: i segnali ignorati ( ) rimangono ignorati nella subshell, ma le altre trap ( SIGNAL ) vengono ripristinate all'azione predefinita 4 .trap '' SIGNAL
trap CODE
Una subshell è quindi diversa dall'esecuzione di uno script. Uno script è un programma separato. Questo programma separato potrebbe anche essere uno script che viene eseguito dallo stesso interprete del genitore, ma questa coincidenza non dà al programma separato alcuna visibilità speciale sui dati interni del genitore. Le variabili non esportate sono dati interni, quindi quando viene eseguito l'interprete per lo script della shell figlio , queste variabili non vengono visualizzate. Le variabili esportate, ovvero le variabili di ambiente, vengono trasmesse ai programmi eseguiti.
Così:
x=1
(echo $x)
stampa 1
perché la subshell è una replica della shell che l'ha generata.
x=1
sh -c 'echo $x'
capita di eseguire una shell come processo figlio di una shell, ma x
sulla seconda riga non c'è più connessione con x
la seconda riga rispetto a
x=1
perl -le 'print $x'
o
x=1
python -c 'print x'
1 Un'eccezione è la ksh93
shell in cui il fork è ottimizzato e la maggior parte dei suoi effetti collaterali sono emulati.
2 Semanticamente, sono copie. Dal punto di vista dell'implementazione, c'è molta condivisione in corso.
3 Per il lato destro, dipende dalla shell.
4 Se lo provi, nota che cose come$(trap)
potrebbero riportare le trappole della shell originale. Nota anche che molte shell hanno bug in casi angolari che coinvolgono trappole. Ad esempio, ninjalj nota che a partire dalla 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) )'
esegue la ERR
trap dalla subshell nidificata nel caso "due subshells", ma non la ERR
trap dalla subshell intermedia - l' set -E
opzione dovrebbe propagare ilERR
trap a tutti i subshells ma la subshell intermedia è ottimizzata e quindi non è lì per eseguire la sua ERR
trap.
x=out; (x=in; echo $x)
)