Perché una variabile è visibile in una subshell?


18

Il Learning Bash Book menziona che una subshell erediterà solo le variabili di ambiente e i descrittori di file, ecc. E che non erediterà le variabili che non vengono esportate:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Come so la shell creerà due subshell per ()e per ./file, ma perché nel ()caso la subshell identifica la varvariabile anche se non viene esportata e nel ./filecaso in cui non l'ha identificata?

# 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

Ho provato ad utilizzare straceper capire come questo accade e sorprendentemente ho trovato che bash userà gli stessi argomenti per la chiamata di sistema clone, quindi questo significa che sia il processo biforcuta in ()e ./filedovrebbe avere lo stesso spazio di indirizzi del processo del genitore, quindi perché nel ()caso la variabile sia visibile alla subshell e lo stesso non accade per il ./filecaso, sebbene gli stessi argomenti siano basati sulla chiamata di sistema clone?


vinc17 dice vero, anche tu ottieni pstree quando hai subshell, credi che sia importante.
Golfo Persico,

Risposte:


15

Il Learning Bash Book è sbagliato. I sottotitoli ereditano tutte le variabili. Anche $$(il PID della shell originale) viene mantenuto. Il motivo è che per una subshell, la shell si biforca e non esegue una nuova shell (al contrario, quando si digita ./file, viene eseguito un nuovo comando, ad esempio una nuova shell; nell'output di strace, guardare execvee simili) . Quindi, in sostanza, è solo una copia (con alcune differenze documentate).

Nota: questo non è specifico per bash; questo è vero per qualsiasi shell.


Oky, ma ho provato ora a lanciare strace sulla shell e ho cercato di eseguire ./file ma non riesco a trovare alcuna chiamata per exec e quindi lo spazio degli indirizzi dovrebbe essere lo stesso per entrambi i processi, quindi come può essere spiegato?
user3718463,

@ user3718463 Hai usato l' -fopzione di stracetracciare anche i bambini? È necessario trovare i dirigenti.
vinc17,

sì, lo capisco grazie mille, mi mancava l'opzione -f, e quindi non riesco a trovare la chiamata exec sys
user3718463,

16

Tu o il libro stai confondendo una subshell con un sottoprocesso che è una shell.

Alcuni costrutti di shell generano il fork di un processo figlio. Sotto Linux, forkc'è un caso speciale della clonechiamata di sistema più generale , che hai osservato nel straceregistro. Il bambino esegue una parte dello script della shell. Il processo figlio è chiamato subshell . Il costrutto più diretto è command1 &: viene command1eseguito in una subshell e i comandi successivi vengono eseguiti nella shell padre. Altri costrutti che creano una subshell includono la sostituzione dei comandi $(command2)e le pipe command3 | command4( command3viene eseguita in una subshell, command4viene eseguita in una subshell nella maggior parte delle shell ma non in ksh o zsh).

Una subshell è una copia del processo genitore, quindi non ha solo le stesse variabili di ambiente, ma anche tutte le stesse definizioni interne: variabili (incluso $$l'ID del processo della shell originale), funzioni, alias, opzioni, ecc. Prima di eseguire il codice nella subshell, bash imposta la variabile BASHPIDsull'ID processo del processo figlio.

Quando si esegue ./file, questo esegue un comando esterno. Innanzitutto, la shell crea un processo figlio; quindi questo processo figlio esegue (con la execvechiamata di sistema) il file eseguibile ./file. Un processo figlio eredita gli attributi di processo dei suoi genitori: ambiente, directory corrente, ecc. Gli aspetti interni dell'applicazione vengono persi nella execvechiamata: variabili, funzioni, ecc. Non esportate sono nozioni di base che il kernel non conosce e si perdono quando bash esegue un altro programma. Anche se quell'altro programma sembra essere uno script bash, viene eseguito da una nuova istanza di bash che non sa o si preoccupa che il suo processo genitore sia anche un'istanza di bash. Pertanto una variabile shell (variabile non esportata) non sopravvive execve.


Questa risposta ha chiarito alcune cose per me. L'unica cosa che non capisco è questa frase nel secondo paragrafo: "Il bambino esegue una parte dello script della shell". A quale script di shell viene fatto riferimento?
flow2k,

@ flow2k Lo script (ovvero il programma) interpretato dalla shell.
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.