Una subshell inizia come una copia quasi identica del processo di shell originale. Sotto il cofano, la shell chiama la forkchiamata 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 BASHPIDil PID del processo shell, che cambia in subshells. Bash, zsh e mksh provvedono $RANDOMa 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 '' SIGNALtrap 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 1perché 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 xsulla seconda riga non c'è più connessione con xla seconda riga rispetto a
x=1
perl -le 'print $x'
o
x=1
python -c 'print x'
1 Un'eccezione è la ksh93shell 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 ERRtrap dalla subshell nidificata nel caso "due subshells", ma non la ERRtrap dalla subshell intermedia - l' set -Eopzione dovrebbe propagare ilERRtrap a tutti i subshells ma la subshell intermedia è ottimizzata e quindi non è lì per eseguire la sua ERRtrap.
x=out; (x=in; echo $x))