Neden bir ortam değişkeni belirleyip onu aynı komut satırında yankılayamıyorum?


91

Bu pasajı düşünün:

$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz

İşte kurdum $SOMEVARiçin AAAilk satırda - ve ben ikinci satırda onu yankı zaman, benim hemen AAAbeklendiği gibi içerikleri.

Ancak değişkeni aynı komut satırında şu şekilde belirtmeye çalışırsam echo:

$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz

... BBBBeklediğim gibi alamıyorum - eski değeri ( AAA) alıyorum .

İşler böyle mi olmalı? Öyleyse, nasıl olur da değişkenleri belirleyebilir LD_PRELOAD=/... program args ...ve çalışmasını sağlayabilirsiniz? Neyi kaçırıyorum?


2
Atamayı ayrı bir ifade yaptığınızda veya kendi ortamıyla bir komut dosyasını çağırdığınızda çalışır, ancak mevcut ortamda bir komutun ön yüzünü oluştururken çalışmaz. İlginç!
Todd A. Jacobs

1
Nedeni LD_PRELOADişleri değişken programın ayarlanmış olmasıdır çevre - değil , komut satırında.
sonraki duyuruya kadar duraklatıldı.

Yanıtlar:


103

Gördüğünüz şey beklenen davranıştır. Sorun, ana kabuğun $SOMEVAR, komutu değiştirilmiş ortamla birlikte çalıştırmadan önce komut satırında değerlendirmesidir . $SOMEVAROrtam ayarlanana kadar ertelenmiş değerlendirmesini almanız gerekir .

Acil seçenekleriniz şunları içerir:

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz.
  2. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'.

Her ikisi de ana kabuğun değerlendirmesini engellemek için tek tırnak kullanır $SOMEVAR; yalnızca ortamda ayarlandıktan sonra değerlendirilir (geçici olarak, tek komut süresince).

Başka bir seçenek (ayrıca önerdiği gibi alt kabuk notasyonu kullanmaktır Marcus Kuhn , onun içinde cevap ):

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

Değişken yalnızca alt kabukta ayarlanır


Harika, @JonathanLeffler - açıklama için çok teşekkürler; şerefe!
sdaau

@ Markus-kuhn'dan yapılan eklemeyi abartmak zordur.
Alex Che

37

Sorun Yeniden Ziyaret Edildi

Açıkçası, kılavuz bu noktada kafa karıştırıcı. GNU Bash kılavuzu diyor ki:

Herhangi bir basit komut veya işlev için ortam [bunun yerleşikleri hariç tuttuğuna dikkat edin], Kabuk Parametrelerinde açıklandığı gibi parametre atamalarının önüne geçici olarak artırılabilir. Bu atama ifadeleri yalnızca o komut tarafından görülen ortamı etkiler.

Eğer gerçekten cümleyi ayrıştırmak değilse, ne söylediğini olmasıdır çevre komut / fonksiyon için ebeveyn süreci için ortam değiştirilmiş, ancak değildir. Yani, bu işe yarayacak:

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

env komutunun ortamı çalıştırılmadan önce değiştirildiğinden. Ancak bu işe yaramayacak:

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

kabuk tarafından parametre yorumlamasının gerçekleştirilmesi nedeniyle.

Tercüman Adımları

Problemin bir başka kısmı da Bash'in yorumlayıcı için şu adımları tanımlamasıdır :

  1. Girdisini bir dosyadan (Kabuk Komut Dosyalarına bakın), -c çağırma seçeneğine argüman olarak sağlanan bir dizeden (Bash'i Çağırmak'a bakın) veya kullanıcının terminalinden okur.
  2. Alıntıda açıklanan alıntı kurallarına uyarak girdiyi kelimelere ve işleçlere böler. Bu belirteçler meta karakterlerle ayrılır. Takma ad genişletme bu adımda gerçekleştirilir (bkz. Diğer Adlar).
  3. Belirteçleri basit ve bileşik komutlara ayırır (bkz. Kabuk Komutları).
  4. Çeşitli kabuk genişletmelerini gerçekleştirir (bkz. Kabuk Genişletmeleri), genişletilmiş belirteçleri dosya adları listelerine (bkz. Dosya Adı Genişletme), komutlar ve bağımsız değişkenlere böler.
  5. Gerekli yeniden yönlendirmeleri gerçekleştirir (bkz. Yeniden Yönlendirmeler) ve yeniden yönlendirme işleçlerini ve bunların işlenenlerini bağımsız değişken listesinden kaldırır.
  6. Komutu yürütür (bkz. Komutları Yürütme).
  7. İsteğe bağlı olarak komutun tamamlanmasını bekler ve çıkış durumunu toplar (bkz. Çıkış Durumu).

Burada olan şey, yerleşiklerin kendi yürütme ortamlarına sahip olmadıkları için değiştirilmiş ortamı asla görmedikleri. Buna ek olarak, basit komutları (örn / bin / eko) do (env örneği çalıştı neden olan) modifiye edilmiş bir ennvironment olsun ama kabuk genişleme gerçekleşiyor akım adımda 4. çevre.

Diğer bir deyişle, 'aaa $ TESTVAR ccc'yi / bin / echo'ya iletmiyorsunuz; enterpolasyonlu dizeyi (mevcut ortamda genişletildiği şekilde) / bin / echo'ya geçiriyorsunuz. Bu durumda, mevcut ortamda TESTVAR olmadığı için, komuta basitçe 'aaa ccc' geçersiniz .

Özet

Belgeler çok daha net olabilir. İyi ki Stack Overflow var!

Ayrıca bakınız

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment


Bunu zaten onaylamıştım - ama bu soruya daha yeni döndüm ve bu yazı tam olarak ihtiyacım olan noktaları içeriyor; çok teşekkürler, @CodeGnome!
sdaau

Bash Bu cevap yayınlanmıştır beri bu alanda değişip değişmediğini bilmiyorum ama öneki değişken atamaları yapmak artık yerleşikleri ile çalışmalarını. Örneğin, beklendiği gibi FOO=foo eval 'echo $FOO'yazdırır foo. Bu, benzer şeyler yapabileceğiniz anlamına gelir IFS="..." read ....
Will Vousden

Bence Bash aslında geçici olarak kendi ortamını değiştiriyor ve komut tamamlandıktan sonra onu geri yüklüyor ki bu tuhaf yan etkilere neden olabilir.
Will Vousden

22

İstediğinizi elde etmek için kullanın

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

Nedeni:

  • Atamayı bir sonraki komuttan noktalı virgülle veya yeni satırla ayırmalısınız, aksi takdirde bir sonraki komut için parametre genişletmesi gerçekleşmeden (echo) çalıştırılmaz.

  • Mevcut satırın ötesinde kalmadığından emin olmak için atamayı bir alt kabuk ortamında yapmanız gerekir.

Bu çözüm, diğerlerinin önerdiğinden daha kısa, daha temiz ve daha etkilidir, özellikle yeni bir süreç yaratmaz.


3
Buraya gelecek olan gelecekteki Google çalışanları için: Bu, muhtemelen bu sorunun en iyi cevabıdır. Daha da karmaşık hale getirmek için, atamanın komut ortamında kullanılabilir olması gerekiyorsa, onu dışa aktarmanız gerekir. Alt kabuk, atamanın devam etmesini hala engelliyor. (export SOMEVAR=BBB; python -c "from os import getenv; print getenv('SOMEVAR')")
eaj

@eaj Örneğinizde olduğu gibi, bir kabuk değişkenini tek bir harici program çağrısına aktarmak için şunu kullanınSOMEVAR=BBB python -c "from os import getenv; print getenv('SOMEVAR')"
Markus Kuhn

10

Bunun nedeni, bunun bir satır için bir ortam değişkeni ayarlamasıdır. Ama echogenişleme bashyapmaz , yapar. Dolayısıyla, sizin değişken aslında komut çalıştırılmadan önce, olsa genişletilir SOME_VARolan BBByankı komuta bağlamında.

Etkiyi görmek için aşağıdaki gibi bir şey yapabilirsiniz:

$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB

Burada değişken, alt süreç yürütülene kadar genişletilmez, bu nedenle güncellenen değeri görürsünüz. SOME_VARIABLEana kabukta tekrar kontrol ederseniz AAA, beklendiği gibi hala kalır .


1
Neden yazıldığı gibi çalışmadığının doğru açıklaması ve uygulanabilir bir çözüm için +1.
Jonathan Leffler

1
SOMEVAR=BBB; echo zzz $SOMEVAR zzz

Kullanın ; aynı satırdaki ifadeleri ayırmak için.


1
Bu işe yarıyor, ancak konu tam olarak bu değil. Buradaki fikir, ortamı çözümünüzün yaptığı gibi kalıcı olarak değil, yalnızca tek bir komut için ayarlamaktır.
Jonathan Leffler

Bunun için teşekkürler @Kyros; Şimdiye kadar bunu nasıl kaçırdım bilmiyorum :) Hala LD_PRELOADnoktalı virgül olmadan çalıştırılabilir bir dosyanın önünde nasıl ve böyle çalışabileceğini dolaşırken ... Tekrar çok teşekkürler - şerefe!
sdaau

@JonathanLeffler - gerçekten de fikir buydu; Noktalı virgülün değişikliği kalıcı kıldığını fark etmemiştim - bunu belirttiğiniz için teşekkürler!
sdaau

1

İşte bir alternatif:

SOMEVAR=BBB && echo zzz $SOMEVAR zzz

Kullanmak İster &&veya ;değil OP'ın istenen davranıştır komutları, atama hakimse, ayırmak için. Markus Kuhn bu cevabın doğru versiyonuna sahip.
eaj
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.