Cakupan apa yang bisa dimiliki oleh variabel shell?

42

Saya hanya mengalami masalah yang menunjukkan bahwa saya tidak jelas tentang cakupan variabel shell.

Saya mencoba menggunakan bundle install, yang merupakan perintah Ruby yang menggunakan nilai $GEM_HOMEuntuk melakukan tugasnya. Saya telah menetapkan $GEM_HOME, tetapi perintah mengabaikan nilai itu sampai saya gunakan export, seperti pada export GEM_HOME=/some/path.

Saya membaca bahwa ini membuat variabel entah bagaimana "global" (juga dikenal sebagai variabel lingkungan ), tetapi saya tidak mengerti apa artinya itu. Saya tahu tentang global dalam pemrograman, tetapi tidak lintas program yang berbeda.

Juga, mengingat bahwa pengaturan saya, variabel tersebut hanya berlaku untuk sesi shell saat ini, bagaimana saya mengaturnya untuk, katakanlah, proses yang dianemonisasi?

Cakupan apa yang bisa dimiliki oleh variabel shell?

Nathan Long
sumber

Jawaban:

33

Prosesnya diatur sebagai pohon: setiap proses memiliki orangtua yang unik, selain inityang PIDselalu 1 dan tidak memiliki orangtua.

Penciptaan proses baru biasanya berjalan melalui sepasang panggilan sistem fork/ execv, di mana lingkungan proses anak adalah salinan dari proses induk.

Untuk menempatkan variabel di lingkungan dari shell Anda harus exportvariabel itu, sehingga terlihat secara rekursif untuk semua anak. Tetapi perlu diketahui bahwa jika anak mengubah nilai variabel, nilai yang diubah hanya terlihat olehnya dan semua proses yang dibuat setelah perubahan itu (menjadi salinan , seperti yang dikatakan sebelumnya).

Perhatikan juga bahwa proses anak dapat mengubah lingkungannya, misalnya dapat mengatur ulang ke nilai default, seperti yang mungkin dilakukan dari loginmisalnya.

enzotib
sumber
1
Ah! OK, mari kita lihat apakah saya mengerti ini. Dalam shell, jika saya katakan FOO=bar, itu menetapkan nilai untuk proses shell saat ini. Jika saya kemudian menjalankan program like ( bundle install), itu menciptakan proses anak, yang tidak mendapatkan akses FOO. Tetapi jika saya katakan export FOO=bar, proses anak (dan turunannya) akan memiliki akses ke sana. Salah satu dari mereka dapat, pada gilirannya, meminta export FOO=buzzuntuk mengubah nilai bagi keturunannya, atau hanya FOO=buzzmengubah nilai hanya untuk dirinya sendiri. Apakah itu benar?
Nathan Long
2
@ NathanLong Bukan itu persisnya: dalam semua shell modern, sebuah variabel diekspor (dan karenanya setiap perubahan nilai tercermin dalam lingkungan keturunan) atau tidak diekspor (artinya variabel tersebut tidak berada di lingkungan). Secara khusus, jika variabel sudah ada di lingkungan ketika shell mulai, itu diekspor.
Gilles 'SO- berhenti bersikap jahat'
2
Saya sedikit bingung dengan kalimat "jika seorang anak mengubah nilai variabel, nilai yang diubah hanya dapat dilihat olehnya dan semua proses dibuat setelah perubahan itu". Akan lebih tepat untuk mengatakan "... terlihat olehnya dan semua proses turunannya dibuat setelah perubahan itu" - anak-anak lain dari proses induk, bahkan yang dimulai setelah proses anak, tidak terpengaruh.
Jaan
26

Setidaknya di bawah kshdan bash, variabel dapat memiliki tiga cakupan, bukan dua seperti semua jawaban yang tersisa mengatakan.

Selain variabel yang diekspor (yaitu lingkungan) dan lingkup variabel shell yang tidak diekspor, ada juga yang lebih sempit ketiga untuk variabel fungsi lokal.

Variabel yang dideklarasikan dalam fungsi shell dengan typesettoken hanya terlihat di dalam fungsi yang dideklarasikan di dalam dan di dalam (sub) fungsi yang dipanggil dari sana.

Ini ksh/ bashkode:

# 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 

menghasilkan output ini:

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

Seperti yang Anda lihat, variabel yang diekspor ditampilkan dari tiga lokasi pertama, variabel yang tidak diekspor tidak ditampilkan di luar shell saat ini dan variabel fungsi lokal tidak memiliki nilai di luar fungsi itu sendiri. Tes terakhir tidak menunjukkan nilai sama sekali, ini karena variabel yang diekspor tidak dibagi di antara shell, yaitu mereka hanya bisa diwarisi dan nilai yang diwarisi tidak dapat dipengaruhi setelahnya oleh shell induk.

Perhatikan bahwa perilaku yang terakhir ini sangat berbeda dari Windows di mana Anda dapat menggunakan Variabel Sistem yang sepenuhnya global dan dibagikan oleh semua proses.

Jlliagre
sumber
12

Mereka dicakup oleh proses

Penjawab lain membantu saya untuk memahami bahwa ruang lingkup variabel shell adalah tentang proses dan turunannya .

Ketika Anda mengetik perintah seperti lspada baris perintah, Anda sebenarnya sedang forking proses untuk menjalankan lsprogram. Proses baru memiliki shell Anda sebagai induknya.

Setiap proses dapat memiliki variabel "lokal" sendiri, yang tidak diteruskan ke proses anak. Itu juga dapat mengatur variabel "lingkungan", yang. Menggunakan exportmenciptakan variabel lingkungan. Dalam kedua kasus tersebut, proses yang tidak terkait (rekan asli) tidak akan melihat variabel; kita hanya mengendalikan apa yang dilihat oleh proses anak.

Misalkan Anda memiliki bash shell, yang akan kita panggil A. Anda mengetik bash, yang membuat shell proses bash anak, yang akan kami panggil B. Apa pun yang Anda panggil exportdi A masih akan diatur dalam B.

Sekarang, di B, katamu FOO=b. Satu dari dua hal akan terjadi:

  • Jika B tidak menerima (dari A) variabel lingkungan yang disebut FOO, itu akan membuat variabel lokal. Anak-anak B tidak akan mendapatkannya (kecuali B memanggil export).
  • Jika B memang menerima (dari A) variabel lingkungan yang dipanggil FOO, ia akan memodifikasinya untuk dirinya sendiri dan selanjutnya anak-anak yang bercabang dua . Anak-anak B akan melihat nilai yang diberikan B. Namun, ini tidak akan memengaruhi A sama sekali.

Ini demo cepat.

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'

Semua ini menjelaskan masalah asli saya: Saya mengatur GEM_HOMEdi shell saya, tetapi ketika saya menelepon bundle install, itu menciptakan proses anak. Karena saya belum pernah menggunakan export, proses anak tidak menerima cangkang GEM_HOME.

Tidak mengekspor

Anda dapat "membatalkan ekspor" suatu variabel - mencegahnya diteruskan ke anak-anak - dengan menggunakan 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
Nathan Long
sumber
1
Ketika Anda mengatakan "itu akan memodifikasinya untuk dirinya sendiri dan anak-anaknya" Anda harus mengklarifikasi bahwa hanya anak-anak yang dibuat setelah modifikasi yang akan melihat nilai yang dimodifikasi.
enzotib
1
@enzotib - poin bagus. Diperbarui.
Nathan Long
3

Penjelasan terbaik yang dapat saya temukan tentang ekspor adalah yang ini:

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

Set variabel dalam subkulit atau kulit anak hanya terlihat oleh subkulit di mana ia didefinisikan. Variabel yang diekspor sebenarnya dibuat menjadi variabel lingkungan. Jadi untuk menjadi jelas Anda bundle installmenjalankan shell sendiri yang tidak melihat $GEM_HOMEkecuali dibuat environmentvariabel alias diekspor.

Anda dapat melihat dokumentasi untuk ruang lingkup variabel di sini:

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

Karlson
sumber
Ah, jadi saya salah menggunakan istilah "variabel lingkungan" untuk FOO=bar; Anda harus menggunakannya exportuntuk membuatnya. Pertanyaan diperbaiki sesuai.
Nathan Long
Lihatlah tautan yang telah saya tambahkan.
Karlson
3

Ada hierarki cakupan variabel, seperti yang diharapkan.

Lingkungan Hidup

Lingkup terluar adalah lingkungan. Ini adalah satu-satunya ruang lingkup yang dikelola oleh sistem operasi dan karenanya dijamin ada untuk setiap proses. Ketika suatu proses dimulai, ia menerima salinan lingkungan orang tuanya setelah itu keduanya menjadi independen: memodifikasi lingkungan anak tidak mengubah orangtua, dan memodifikasi lingkungan orangtua tidak mengubah lingkungan anak yang sudah ada.

Variabel shell

Kerang memiliki gagasan variabel mereka sendiri. Di sinilah segalanya mulai menjadi sedikit membingungkan.

Saat Anda menetapkan nilai ke variabel di shell, dan variabel itu sudah ada di lingkungan, variabel lingkungan menerima nilai baru. Namun jika variabel tersebut tidak ada di lingkungan namun itu menjadi variabel shell . Variabel Shell hanya ada dalam proses shell, mirip dengan bagaimana variabel Ruby hanya ada dalam skrip Ruby. Mereka tidak pernah diwarisi oleh proses anak.

Di sinilah exportkata kunci berperan. Ini menyalin variabel shell ke lingkungan proses shell sehingga memungkinkan proses anak untuk mewarisi.

Variabel lokal

Variabel lokal adalah variabel shell yang dicakup ke blok kode yang berisi mereka. Anda mendeklarasikan variabel lokal dengan typesetkata kunci (portabel) atau localatau declare(Bash). Seperti variabel shell lainnya, variabel lokal tidak diwariskan oleh proses anak. Juga variabel lokal tidak dapat diekspor.

Ular
sumber