Ce scopuri pot avea variabilele shell?

Tocmai am întâmpinat o problemă care îmi arată că nu sunt clar asupra sferei variabilelor shell.

Încercam să utilizați bundle install, care este o comandă Ruby care folosește valoarea $GEM_HOME pentru a-și face munca. Am setat $GEM_HOME, dar comanda a ignorat acea valoare până când am folosit export, ca în export GEM_HOME=/some/path.

Am citit că acest lucru face ca variabila să fie oarecum „globală” (cunoscută și sub numele de variabilă de mediu ), dar nu „t înțelegeți ce înseamnă asta. Știu despre programe globale în programare, dar nu și între programe distincte.

De asemenea, având în vedere că setarea mea de astfel de variabile se aplică doar sesiunii curente de shell, cum le-aș seta pentru, să zicem, un proces demonizat?

Ce scopuri pot avea variabilele shell?

Răspuns

Procesele sunt organizate ca un copac: fiecare proces are un părinte unic, în afară de init care PID este întotdeauna 1 și nu are părinte.

Crearea unui nou proces trece în general printr-o pereche de fork / , în care mediul procesului copil este o copie a procesului părinte.

Pentru a pune o variabilă în mediu din shell trebuie să export acea variabilă, astfel încât să fie vizibilă recursiv pentru toți copiii. Rețineți însă că, dacă un copil schimbă valoarea unei variabile, valoarea modificată este vizibilă doar pentru aceasta și toate procesele create după care se schimbă (fiind o copie , ca anterior said).

Luați în considerare, de asemenea, faptul că un proces copil ar putea schimba mediul său, de exemplu, ar putea să-l resetați la valorile implicite, așa cum se face probabil din login pentru exemplu.

Comentarii

  • Ah! OK, să vedem ‘ dacă înțeleg asta. În shell, dacă spun FOO=bar, setează valoarea procesului curent de shell. Dacă atunci rulez un program ca (bundle install), se creează un proces copil, care nu ‘ nu are acces la FOO. Dar dacă aș fi spus export FOO=bar, procesul copil (și descendenții săi) ar avea acces la el. Unul dintre ei ar putea, la rândul său, să apeleze export FOO=buzz pentru a schimba valoarea descendenților săi sau doar FOO=buzz pentru a schimba valoarea numai pentru sine . Este adevărat?
  • @NathanLong Că ‘ nu este exact: în toate shell-urile moderne, fie o variabilă este exportată (și, prin urmare, orice modificare a valorii este reflectată în mediul descendenților) sau neexportată (ceea ce înseamnă că variabila nu se află în mediu). În special, dacă variabila se află deja în mediu când pornește shell-ul, aceasta este exportată.
  • Am fost puțin confuz de propoziția ” dacă un copil modificați valoarea unei variabile, valoarea modificată este vizibilă numai pentru aceasta și toate procesele create după această modificare „. Ar fi mai corect să spunem ” … vizibil pentru acesta și toate procesele descendente create după acea modificare ” – cealaltă copiii procesului părinte, chiar și cei declanșați după procesul copil, nu sunt afectați.

Răspuns

La cel puțin sub ksh și bash, variabilele pot avea trei scopuri, nu două precum toate răspunsurile rămase spun în prezent.

În în plus față de variabila exportată (adică de mediu) și domeniile variabile shell neexportate, există și o a treia mai restrânsă pentru variabilele locale funcționale.

Variabile declarate în funcțiile shell cu typeset token sunt vizibile numai în interiorul funcțiilor în care sunt declarate și în (sub) funcțiile apelate de acolo.

Acest ksh / bash cod:

# Create a shell script named /tmp/show that displays the scoped variables values. echo "echo [$environment] [$shell] [$local]" > /tmp/show chmod +x /tmp/show # Function local variable declaration function f { typeset local=three echo "in function": . /tmp/show } # Global variable declaration export environment=one # Unexported (i.e. local) variable declaration shell=two # Call the function that creates a function local variable and # display all three variable values from inside the function f # Display the three values from outside the function echo "in shell": . /tmp/show # Display the same values from a subshell echo "in subshell": /tmp/show # Display the same values from a disconnected shell (simulated here by a clean environment start) echo "in other shell" env -i /tmp/show 

produce această ieșire:

in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] [] 

După cum puteți vedea, variabila exportată este afișată din primele trei locații, variabilele neexportate nu sunt afișate în afara shell-ului curent și variabila locală a funcției nu are valoare în afara funcției în sine. Ultimul test nu arată deloc valori, acest lucru se datorează faptului că variabilele exportate nu sunt partajate între shell-uri, adică pot fi moștenite doar și valoarea moștenită nu poate fi afectată ulterior de shell-ul părinte.

Rețineți că acest ultim comportament este destul de diferit de cel Windows, unde puteți utiliza variabile de sistem care sunt complet globale și partajate de toate procesele.

Răspuns

Acestea sunt definite în funcție de proces

Ceilalți respondenți m-au ajutat să înțeleg că domeniul variabilelor shell este despre și descendenții lor .

Când tastați o comandă precum ls pe linia de comandă, sunteți de fapt bifând un proces pentru a rula programul ls. Noul proces are shell-ul ca părinte.

Orice proces poate avea propriile variabile „locale”, care sunt nu este transmisă proceselor copil. Poate seta, de asemenea, variabile de „mediu”. Utilizarea export creează o variabilă de mediu. caz, procesele nelegate (colegii originalului) nu vor vedea variabila; controlăm doar ce proc proc vezi.

Să presupunem că ai un shell bash, pe care îl vom numi A. Tastați bash , care creează un shell shell bash proces, pe care îl vom numi B. Orice lucru pe care l-ați numit export în A va fi setat în continuare în B.

Acum, în B, spuneți FOO=b. Unul dintre cele două lucruri se va întâmpla:

  • Dacă B nu a primit (de la A) o variabilă de mediu numită FOO, va crea o variabilă locală. Copiii lui B nu îl vor primi (cu excepția cazului în care B apelează export).
  • Dacă B a făcut primi (de la A) o variabilă de mediu numită FOO, o va modifica pentru sine și copiii ei furcați ulterior . Copiii lui B vor vedea valoarea atribuită de B. Cu toate acestea, acest lucru nu va afecta deloc A. A

Aici este o demonstrație rapidă .

FOO=a # set "local" environment variable echo $FOO # "a" bash # forks a child process for the new shell echo $FOO # not set exit # return to original shell echo $FOO # still "a" export FOO # make FOO an environment variable bash # fork a new "child" shell echo $FOO # outputs "a" FOO=b # modifies environment (not local) variable bash # fork "grandchild" shell echo $FOO # outputs "b" exit # back to child shell exit # back to original shell echo $FOO # outputs "a" 

Toate acestea explică problema mea inițială: am setat GEM_HOME în shell, dar când am sunat bundle install, care a creat un proces copil. Deoarece nu am folosit export, procesul copil nu a primit shell-ul „s GEM_HOME.

Anularea exportului

Puteți „anula exportul” unei variabile – împiedicați-o să fie transmisă copiilor – utilizând export -n FOO.

export FOO=a # Set environment variable bash # fork a shell echo $FOO # outputs "a" export -n FOO # remove environment var for children bash # fork a shell echo $FOO # Not set exit # back up a level echo $FOO # outputs "a" - still a local variable 

Comentarii

  • Când spui ” o va modifica pentru sine și copiii săi ” ar trebui să clarificați că numai copiii au creat după modificarea va vedea valoarea modificată.
  • @enzotib – punct bun. Actualizat.

Răspuns

Cea mai bună explicație pe care o pot găsi despre export este aceasta:

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

Variabila setată într-un subshell sau un shell copil este vizibilă doar de sub-coaja în care este definită. Variabila exportată este de fapt făcută pentru a fi o variabilă de mediu. Deci, pentru a fi clar bundle install își execută propriul shell, care nu vede $GEM_HOME decât dacă este făcut environment variabilă, de asemenea, exportată.

Puteți consulta documentația pentru domeniul variabil aici:

http://www.tldp.org/LDP/abs/html/subshells.html

Comentarii

  • Ah, așa că am fost incorect să folosesc termenul ” variabilă de mediu ” pentru FOO=bar; trebuie să utilizați export ca să fie una. Întrebarea corectată în consecință.
  • Aruncați o privire la linkul pe care l-am adăugat.

Răspuns

Există o ierarhie de domenii variabile, așa cum era de așteptat.

Mediu

Domeniul exterior este mediul. Acesta este singurul domeniu este gestionat de sistemul de operare și, prin urmare, este garantat că există pentru fiecare proces. Când este început un proces, acesta primește un copie a mediului părintești după care cei doi devin independenți: modificarea mediului copilului nu schimbă părinții, iar modificarea mediului părintelui nu modifică cel al unui copil deja existent.

Variabile Shell

Shells au propria noțiune de variabile. Aici lucrurile încep să devină puțin confuze.

Când atribuiți o valoare unei variabile dintr-un shell, iar acea variabilă există deja în mediu, variabila de mediu primește noua valoare. Cu toate acestea, dacă variabila nu este încă în mediu, ea devine o variabilă shell . Variabilele Shell există numai în cadrul procesului shell, similar cu modul în care variabilele Ruby există doar într-un script Ruby. Nu sunt niciodată moștenite de procesele copilului.

Aici apare cuvântul cheie export. Copiază o variabilă shell în mediul procesului shell, ceea ce face posibilă moștenirea proceselor copil.

Variabile locale

Variabilele locale sunt variabile de tip shell acoperite de blocurile de cod care le conțin. Declarați variabile locale cu cuvântul cheie typeset (portabil) sau local sau declare (Bash ). Ca și alte variabile shell, variabilele locale nu sunt moștenite de procesele copil. De asemenea, variabilele locale nu pot fi exportate.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *