Neden bir komuttan önce değişken ayarlamak bash'ta yasaldır?


68

Yapıyı kullanan sınırlı bir metin dosyasını ayrıştırmak gibi birkaç yanıtla karşılaştım :

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

nerede IFSdeğişken önce ayarlanır readkomuta.

Bash referansını okudum ama bunun neden yasal olduğunu çözemiyorum.

denedim

$ x="once upon" y="a time" echo $x $y

bash komut isteminden ancak hiçbir şey yankılanmadı. Birisi beni bu sözdiziminin IFS değişkeninin bu şekilde ayarlanmasına izin veren referansta tanımlandığı yere işaret edebilir mi? Özel bir durum mu, yoksa diğer değişkenlerle benzer bir şey yapabilir miyim?


Yanıtlar:


69

Basit Komutlar, Kabuk Dilbilgisi altında (vurgu vurgulu):

Basit bir komut, isteğe bağlı değişken atamalarının bir sırası, ardından boş ayrılmış sözcükleri ve yönlendirmeleri takip eder ve bir kontrol operatörü tarafından sonlandırılır. İlk kelime yürütülecek komutu belirtir ve argüman sıfır olarak geçirilir. Kalan sözcükler invoked komutuna argüman olarak iletilir.

Böylece istediğiniz değişkeni geçebilirsiniz. Örneğiniz echoçalışmıyor çünkü değişkenler kabuğa ayarlanmadı. Kabuk genişler $xve komutu çağırmadan $y önce . Bu işe yarar, örneğin:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time

Teşekkürler! Sormadan önce çok fazla googling yaptım ve referansta nerede şanssız olduğunu söylemeye çalışıyordum. Bash scriptleri yazarken daha iyi olmaya çalışıyorum ve cevabınız yardımcı oluyor.
Mike Lippert

1
Hmm Daha iyi bir referans bulmam gerektiğini düşünüyorum, benimki (yukarıdaki bağlantıya bakın) bir Kabuk Dilbilgisi bölümü olmadığını söylüyor.
Mike Lippert

1
@MikeLippert Bu referansta 3.7.4'e bakınız ("Herhangi bir basit komut veya fonksiyonun ortamı, parametre atamalarına önek ekleyerek geçici olarak artırılabilir"). Bence bu referans bash'ın eski bir versiyonundan. Sadece man bashsistemimde koştum ...
derobert

29

Tanımlanan değişkenler, çatal işlemindeki ortam değişkenleri gibidir.

Eğer koşarsan

A="b" echo $A

daha sonra ilk Bash genişler $Aiçine ""ve daha sonra çalıştırır

A="b" echo

İşte doğru yol:

x="once upon" y="a time" bash -c 'echo $x $y'

Tek tırnak içindeki alıntılara dikkat edin bash -c, aksi takdirde yukarıdakiyle aynı sorun yaşarsınız.

Bu nedenle döngü örneğiniz yasaldır, çünkü bash yerleşik 'read' komutu, ortam değişkenlerinde IFS'yi arar ve bulur ,. Bu nedenle,

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

basacak TEST is and I is test

Son olarak, sözdizimi için, bir for döngüsünde bir dizge beklenir. Bu nedenle komut vermek için backticks kullanmak zorunda kaldım. Ancak, döngüler gibi komut sözdizimini beklerler IFS=, read xx yy zz.


1
Teşekkürler, cevabınızdan sorduğumdan biraz daha fazlasını öğrendim. Ben de cevabınızı oylardım, ancak henüz izin vermedim ve ilk cevapta kabul edilen cevabı işaretledim.
Mike Lippert

1
Teşekkürler, umduğum şey buydu. Ve bir oylama, takdirinizi duymaktan daha az anlamlıdır!
Mike Fairhurst

Yorumunuzu ilk kod satırı altında açıklığa kavuşturmak için: Evet, unset Adeğişkeni bash $Aboş bir dizgeye genişler ancak karışıklığı önlemek ""için, kod eşdeğer olmadığı için kullanmazdım A="b" echo "". Tartışılmayacak echo.
pabouk

8

man bash

ÇEVRE, ORTAM

[...] Herhangi bir basit komut veya fonksiyonun ortamı, yukarıda PARAMETERS'da açıklandığı gibi parametre atamalarının öneki ile geçici olarak artırılabilir. Bu atama ifadeleri yalnızca bu komut tarafından görülen ortamı etkiler.

Değişken atama gerçekleşmeden önce değişkenler genişletilir. Bunun açık bir var=xşekilde başka şekilde de işe yarayacağını, ancak işe yaramayacağını var=$othervaraçıkladı. Yani $x, müsait olmadan önce senin ihtiyacın var. Ancak bu asıl sorun değil. Asıl sorun, komut satırının yalnızca kabuk ortamı tarafından değiştirilebilmesi, ancak atamanın kabuk ortamının bir parçası olmamasıdır.

Özelliklere karıştırıyorsunuz: Bir komut satırı değişimi istiyor ancak değişken tanımını komutlar ortamına yerleştirdiniz. Komut satırı değişimlerinin kabuk tarafından yapılması gerekir. Ortam, çağrılan komut tarafından açıkça kullanılmalıdır. Bunun nasıl yapılıp yapılmayacağı komuta bağlıdır.

Bu kullanımın avantajı, kabuk ortamını etkilemeden bir alt işlem ortamını ayarlayabilmenizdir.

x="once upon" y="a time" bash -c 'echo $x $y'

Beklediğiniz gibi çalışır, çünkü bu durumda her iki özellik de birleştirilir: Komut satırının değiştirilmesi, çağrı kabuğu tarafından değil, alt işlem kabuğu tarafından yapılır.


1
Bundan biraz daha ince, çünkü örnek aynı zamanda bir yerleşik x="once upon" y="a time" eval 'echo $x $y'olduğundan beri alt süreç olmadığında da işe yarıyor eval. Sanırım manpage'den gelen alıntı The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments. Sorunun örneğini göz önünde bulundurarak, bu şekilde olması gerekir, çünkü readaynı zamanda bir yerleşiktir ve geçici olarak değiştirilmiş bir durumla çalışır IFS.
David Ongaro

4

Sağladığınız komut nedeniyle farklıdır $xve $ygenişletilir önceecho komut çalışır, bu yüzden onların değerleri geçerli kabuk değil, değerler kullanılır echogörünmesini olsaydı ortamındaki görecekti.


Komut çalıştırıldıktan sonra komut satırındaki herhangi bir şey nasıl genişletilebilir?
Hauke ​​Laging

Önceden komut atamaları için xve yçevre için vardır echo, içinde çeşitli argümanların değil çevreyi çalışır echogenişletilir. Bunun için IFS=, read xx yy zzdizgenin tamamı readkomut tarafından okunmamış olarak okunur . Daha sonra , bu dizi değerine göre bölünür IFStahsis karşılık gelen parçalar ile xx, yyve zz.
chepner

Ben sadece "komut çalıştırılmadan önce genişleyen" ifadesinin pek mantıklı gelmediğini belirtmek istedim çünkü komutun başlamasından sonra hiçbir şey daha fazla genişletilemedi. Ayrıca: Cevabımı tek bir gözle görmedin mi ya da yine de neler olduğunu açıklamaya ihtiyacım olduğuna inanıyor musun?
Hauke ​​Laging

1
Ne bir açıklamaya ihtiyacınız olduğunu ne de komut çalıştırıldıktan sonra herhangi bir şeyin genişletilebileceğini iddia etmedim. Ancak, bashönce verilen komut satırını ayrıştırır, gelen komutun ortamına uygulanacak iki değişken ataması olduğunu kabul eder, run ( echo) komutunu tanımlar , argümanlarda bulunan parametreleri genişletir, sonra komutu çalıştırır. echogenişletilmiş argümanlar ile.
chepner

Durumda echo, değişkeni "görebilecek" olup olmadığından emin değildim, çünkü bu bir yerleşik komuttur ve bu nedenle kendi ortamına sahip bir alt kabukta çalışmaz. Ama ben evalde bir yerleşik olan ile denedim ve gerçekten bunu biliyor. Örn a=xyz eval 'echo $BASHPID $a; grep -z ^a /proc/$BASHPID/{,task/*}/environ'; echo $BASHPID $a; abunun sadece evalpid aynı olmasına ve çevre değerlendirme sırasında değişmemesine rağmen içinde olduğunu gösteren deneyin ! ( /procSize erişmek için bunu Linux altında çalıştırmanız gerekir.) Burada bash biraz ek sihir yapıyor gibi görünüyor.
David Ongaro

2

" Neden yasal olduğunu" daha büyük resmi için gidiyorum

Cevap: Bir programı arayabilmeniz veya çağırabilmeniz için ve bu başlatma için yalnızca belirli bir değişkene sahip bir değişken kullanın.

Örnek olarak: 'db_connection' adında bir veritabanı bağlantısı için bir param var ve normal olarak test veritabanı bağlantınızın adı olarak 'test'i geçersiniz. Aslında, daha sonra açıkça girmeniz gerekmeyen bir varsayılan olarak bile ayarlayabilirsiniz. Ancak bazen ci veritabanıyla çalışmak istersiniz. Böylece paramda 'ci' olarak geçersiniz ve çağrılan program tüm veritabanı çağrıları için kullanılacak db'nin adı olarak o veritabanı paramını kullanır . Bir sonraki çalıştırmada, yaklaşımı tekrarlamaz ve sadece programı çağırırsanız, değişken önceki varsayılan değerine geri döner.


0

Ayrıca kullanabilirsiniz ;. Komut ayırıcı olduğu için daha önce değerlendirilecek.

x="once upon" y="a time"; echo $x $y

1
Bu iki kabuk değişkeni yaratır ve verilen yardımcı programda ortam değişkenleri oluşturmaz. Bu çok farklı bir şey. Ayrıca, şu anki kabuğun içinde yeni değişkenlerin olduğu yan etkisi de var.
Kusalananda
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.