Da quello che ho letto, mettere un comando tra parentesi dovrebbe eseguirlo in una subshell, simile all'esecuzione di uno script. Se questo è vero, come fa a vedere la variabile x se x non è esportata?
x=1
Esecuzione di (echo $x)
sulla riga di comando risulta 1
Esecuzione di echo $x
in uno script non risulta nulla, come previsto
Risposta accettata:
Una subshell inizia come una copia quasi identica del processo della shell originale. Sotto il cofano, la shell chiama il fork
chiamata di sistema, che crea un nuovo processo il cui codice e memoria sono copie. Quando viene creata la subshell, ci sono pochissime differenze tra essa e il suo genitore. In particolare, hanno le stesse variabili. Anche il $$
la variabile speciale mantiene lo stesso valore nelle subshell:è 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
al PID del processo shell, che cambia nelle subshell. Bash, zsh e mksh organizzano $RANDOM
per 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 delle funzioni, le definizioni degli alias, le opzioni della shell e anche altre impostazioni vengono ereditate.
Una sottoshell creata da (…)
ha gli stessi descrittori di file del suo creatore. Alcuni altri mezzi per creare subshell modificano alcuni descrittori di file prima di eseguire il codice utente; ad esempio, il lato sinistro di una pipe scorre in una subshell 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 trap personalizzate:segnali ignorati (trap '' SIGNAL
) rimangono ignorati nella subshell, ma altre trap (trap CODE
SEGNALE ) vengono ripristinati all'azione predefinita.
Una subshell è quindi diversa dall'esecuzione di uno script. Uno script è un programma separato. Questo programma separato potrebbe casualmente essere anche 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, non vede queste variabili. Le variabili esportate, ovvero le variabili di ambiente, vengono trasmesse ai programmi eseguiti.
Quindi:
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 il x
sulla seconda riga non ha più alcun collegamento con il x
sulla seconda riga che in
x=1
perl -le 'print $x'
o
x=1
python -c 'print x'
Un'eccezione è ksh93
shell in cui il fork è ottimizzato e la maggior parte dei suoi effetti collaterali vengono emulati.
Semanticamente, sono copie. Dal punto di vista dell'implementazione, c'è molta condivisione in corso.
Per il lato destro, dipende dalla shell.
Se lo provi, tieni presente che cose come $(trap)
può segnalare le trappole della shell originale. Nota anche che molte shell hanno bug nei casi d'angolo che coinvolgono trappole. Ad esempio ninjalj nota che a partire da 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 il ERR
trap dalla subshell nidificata nel caso "due subshell", ma non il ERR
trap dalla subshell intermedia — set -E
l'opzione dovrebbe propagare il ERR
trap a tutte le subshell ma la subshell intermedia è ottimizzata e quindi non è lì per eseguire il suo ERR
trappola.