Kabuk değişkenleri hangi kapsamlarda olabilir?


42

Sadece kabuk değişkenlerinin kapsamı konusunda net olmadığımı gösteren bir sorunla karşılaştım.

Ben kullanım çalışıyordu bundle installdeğerini kullanan bir Ruby komut, $GEM_HOMEişini yapmak için. Ayarlamıştım $GEM_HOME, ancak komut export, kullandığım kadar , bu değeri yoksaydı export GEM_HOME=/some/path.

Bunun değişkeni bir şekilde "global" ( çevre değişkeni olarak da bilinir) yaptığını okudum , ancak bunun ne anlama geldiğini anlamadım. Programlamadaki küreselleri biliyorum, ancak farklı programlar arasında değil.

Ayrıca, bu değişkenler ayarımın yalnızca geçerli kabuk oturumu için geçerli olduğu göz önüne alındığında, onları bir cansız süreç için nasıl ayarlarım?

Kabuk değişkenleri hangi kapsamlarda olabilir?

Yanıtlar:


33

Süreçler bir ağaç olarak düzenlenmiştir: Her işlem dışında eşsiz bir ebeveyni vardır initki PIDher zaman 1'dir ve üstü yoktur.

Yeni bir sürecin oluşturulması genellikle alt sürecin ana işleminin bir kopyası olduğu bir çift fork/ execvsistem çağrısından geçer .

Ortamdaki bir değişkeni kabuktan koymak için, exporto değişkeni yapmanız gerekir , böylece tekrarlayan şekilde tüm çocuklar tarafından görülebilir. Ancak, bir çocuğun bir değişkenin değerini değiştirmesi durumunda, değiştirilen değerin yalnızca kendisine ve bu değişiklikten sonra yaratılan tüm işlemlere görünebileceğini unutmayın ( daha önce belirtildiği gibi bir kopya ).

Ayrıca bir alt sürecin ortamını değiştirebileceğini, örneğin, örneğin büyük olasılıkla yapıldığı gibi varsayılan değerlere sıfırlayabildiğini göz önünde bulundurun login.


1
Ah! Tamam, bakalım bunu anladıysam. Kabukta, söylersem FOO=bar, geçerli kabuk işleminin değerini belirler. Daha sonra ( bundle install) gibi bir program çalıştırırsam , bu erişemeyeceği bir alt süreç oluşturur FOO. Fakat eğer söyleseydimexport FOO=bar , çocuk sürecinin (ve soyundan gelenlerin) buna erişimi olacaktı. Bunlardan biri export FOO=buzz, soyundan gelenlerin değerini değiştirme çağrısı yapabilir ya da sadece FOO=buzzdeğerini kendisi için değiştirebilir. Bu doğru mu?
Nathan Long

2
@NathanLong Tam olarak bu değil: tüm modern kabuklarda, bir değişken ya ihraç edilir (ve böylece değerdeki herhangi bir değişiklik inenlerin ortamına yansır) ya da dışa aktarılmaz (değişkenin çevrede olmadığı anlamına gelir). Özellikle, kabuk başladığında değişken zaten çevrede ise, dışa aktarılır.
Gilles 'SO- kötülük' dur

2
"Bir değişkenin değerini değiştiren bir çocuk değiştirirse, değiştirilen değer yalnızca kendisine ve bu değişiklikten sonra oluşturulan tüm işlemlere görünür" cümlesiyle kafam biraz karıştı. "... ve bu değişimin ardından yaratılan soyundan gelen tüm süreçlere görünür" demek daha doğru olacaktır - ebeveyn sürecinin diğer çocukları, çocuk sürecinden sonra başlayanlar bile etkilenmez.
Jaan

26

En azından kshve altında bash, değişkenlerin üç kapsamı olabilir; kalan tüm cevapların şu anda söylediği gibi ikisi değil .

Dışa aktarılan (yani ortam) değişkenine ve kabuk desteklenmeyen değişken kapsamlarına ek olarak, işlev yerel değişkenleri için üçüncü bir daha dar olanı da vardır.

typesetToken ile kabuk fonksiyonlarında bildirilen değişkenler , sadece oradan çağrılan (alt) fonksiyonlarda ve içinde tanımlandıkları fonksiyonların içinde görülür.

Bu ksh/ bashkod:

# 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 

bu çıktıyı üretir:

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

Gördüğünüz gibi, dışa aktarılan değişken ilk üç konumdan görüntülenir, dışa aktarılan değişkenler geçerli kabuğun dışında görüntülenmez ve yerel değişken fonksiyonun dışında bir değere sahip değildir. Son test hiçbir değer göstermez, çünkü dışa aktarılan değişkenler kabuklar arasında paylaşılmaz, yani yalnızca miras alınabilir ve miras alınan değer daha sonra ana kabuktan etkilenemez.

Bu ikinci davranışın, tamamen global olan ve tüm işlemler tarafından paylaşılan Sistem Değişkenlerini kullanabileceğiniz Windows eyleminden oldukça farklı olduğunu unutmayın.


12

İşlem tarafından kapsanıyorlar

Diğer cevaplayıcılar kabuk değişkeni kapsamının süreçler ve onların torunları ile ilgili olduğunu anlamama yardımcı oldu .

lsKomut satırında olduğu gibi bir komut yazdığınızda, aslında lsprogramı çalıştırmak için bir işlem öngörüyorsunuz . Yeni işlem, kabuğunuzun ebeveyni olarak sahip.

Herhangi bir sürecin alt süreçlere geçmeyen kendi "yerel" değişkenleri olabilir. Ayrıca "ortam" değişkenlerini de ayarlayabilir. Kullanarak exportbir ortam değişkeni yaratır. Her iki durumda da, ilgisiz işlemler (orijinalin eşleri) değişkeni görmeyecektir; Biz sadece çocuk süreçlerinin ne gördüğünü kontrol ediyoruz.

Eğer biz Daktilo A. arayacağım bir bash kabuğunu, varsayalım basharadığınız biz B. Anything arayacağım Bir çocuk bash kabuğunu oluşturur, exporthala B'de kurulacak A'da üzerinde

Şimdi, B de diyorsun FOO=b. İki şeyden biri olacak:

  • Eğer B (A'dan) bir ortam değişkeni almadıysa FOO, yerel bir değişken yaratacaktır. B çocukları alamayacak (B çağırmazsa export).
  • B Eğer ki bir ortam değişkeni seslenmek (A'dan) alacak FOO, bu olacak kendine ve sonradan çatallı çocuklar için değiştirmek . B çocukları B'nin atadığı değeri göreceklerdir. Ancak, bu A'yı hiçbir şekilde etkilemeyecektir.

İşte hızlı bir demo.

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'

Bütün bunlar benim asıl sorunumu açıklıyor: Kabuğuma koydum GEM_HOME, ama aradığımda bundle installçocuk süreci yarattı. Kullanmadığım exportiçin çocuk işlemesi kabukları alamadı GEM_HOME.

Un ihraç

Bir değişkeni "dışa aktarabilir" - kullanarak çocuklara aktarılmasını önleyebilirsiniz - 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

1
"Kendisi ve çocukları için değiştirir" derken, yalnızca değişiklikten sonra oluşturulan çocukların değiştirilen değeri göreceğini açıklığa kavuşturmalısınız .
enzotib

1
@enzotib - iyi nokta. Güncellenmiş.
Nathan Long

3

İhracat hakkında bulabildiğim en iyi açıklama şudur:

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

Bir alt kabuk veya alt kabuk içinde ayarlanan değişken yalnızca tanımlandığı alt kabuk için görülebilir. Dışa aktarılan Değişken aslında bir ortam değişkeni olarak yapılır. Açıkça bundle installsöylemek gerekirse $GEM_HOME, environmentdışa aktarılan bir değişken olmadıkça , kendi kabuğunu çalıştırmaz .

Burada değişken kapsam belgelerine bir göz atabilirsiniz:

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


Ah, "ortam değişkeni" terimini kullanmak için yanlıştım FOO=bar; Bunu exportyapmak için kullanmak zorundasın . Soru buna göre düzeltildi.
Nathan Long

Eklediğim bağlantıya bir bak.
Karlson

3

Beklendiği gibi değişken kapsamlar hiyerarşisi var.

Çevre, ortam

En dış kapsam çevredir. İşletim sistemi tarafından yönetilen tek kapsam budur ve bu nedenle her işlem için var olacağı garanti edilir. Bir süreç başladığında, ebeveyninin çevresinin bir kopyasını alır, daha sonra ikisi bağımsız olur: çocuğun çevresini değiştirmek ebeveynleri değiştirmez ve ebeveynin ortamını değiştirmek halihazırda var olan çocuğunkileri değiştirmez.

Kabuk değişkenleri

Kabuklar kendi değişken kavramlarına sahiptir. Burası işlerin biraz kafa karıştırıcı olmaya başladığı yer.

Kabuktaki bir değişkene bir değer atadığınızda ve bu değişken ortamda zaten mevcutsa, ortam değişkeni yeni değeri alır. Bununla birlikte, değişken ortamda henüz değilse, bir kabuk değişkeni olur . Shell değişkenleri, yalnızca Ruby değişkenlerinin bir Ruby komut dosyasında var olmalarına benzer şekilde, kabuk işlemi içinde bulunur. Asla çocuk süreçleri tarafından miras alınmazlar.

İşte exportanahtar kelimenin devreye girdiği yer. Bir kabuk değişkenini kabuk sürecinin ortamına kopyalar ve alt işlemlerin kalıtımsal olmasını sağlar.

Yerel değişkenler

Yerel değişkenler, onları içeren kod bloklarıyla kaplanan kabuk değişkenleridir. Yerel değişkenleri typeset(taşınabilir) veya localveya declare(Bash) anahtar kelimesiyle ilan edersiniz . Diğer kabuk değişkenleri gibi yerel değişkenler de alt süreçler tarafından kalıtsal değildir. Ayrıca yerel değişkenler dışa aktarılamaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.