"Echo $ (stuff)" veya "echo` stuff "ile ilgili yanlış olan nedir?


31

Aşağıdakilerden birini kullandım

echo $(stuff)
echo `stuff`

( stufförneğin, örneğin pwdveya datedaha karmaşık bir şey).

Sonra bu sözdiziminin yanlış olduğu, kötü bir uygulama, zarif olmayan, aşırı, gereksiz, aşırı karmaşık, kargo kültü programlaması, noobish, naif vb.

Ama komuta yapar , işleri, tam olarak ne sorunu nedir?


11
Gereksizdir ve bu nedenle kullanılmamalıdır. Yararsız Kedinin Kullanımı ile Karşılaştırın: porkmail.org/era/unix/award.html
dr01

7
echo $(echo $(echo $(echo$(echo $(stuff)))))Ayrıca yapar , doğru çalışma ve hala muhtemelen kullanmak ister? ;-)
Peter - Monica

1
Bu genişleme ile tam olarak ne yapmaya çalışıyorsunuz?
curiousguy

@curiousguy Diğer kullanıcılara yardım etmeye çalışıyorum, bu yüzden aşağıdaki cevabım. Son zamanlarda, bu sözdizimini kullanan en az üç soru gördüm. Yazarları yapmaya çalışıyorlardı… çeşitli stuff. : D
Kamil Maciorowski 29:18

2
Ayrıca, hepimiz kendini bir yankı odası ile sınırlamanın akıllıca olmadığını kabul etmiyor muyuz? ;-)
Peter - Monica

Yanıtlar:


63

tl; Dr.

Bir tek stuffolurdu muhtemelen senin için çalışıyorum.



Tam cevap

Ne oluyor

Koşarken foo $(stuff), olan bu:

  1. stuff koşar;
  2. çıktısı (stdout), yazdırılmak yerine $(stuff), çağrısında bulunur foo;
  3. sonra fooçalışır, komut satırı argümanları açıkça stuffgeri dönenlere bağlıdır .

Bu $(…)mekanizmaya "komut değiştirme" denir. Senin durumunda ana komut, echotemelde komut satırı argümanlarını stdout'a basan komuttur. Bu yüzden stuffstdout'a yazdırmaya çalışanlar yakalanır, iletilir echove stdout'a yazdırılır echo.

stuffÇıktısının stdout'a yazdırılmasını istiyorsanız , yalnızca tabanı çalıştırın stuff.

`…`Sözdizimi ile aynı amaca hizmet eder $(…)(aynı adla: "komutu ikamesi") körü körüne onları birbirleriyle karıştırmayın böylece olsa birkaç farklılık vardır. Bkz bu SSS ve bu soruyu .


Ne echo $(stuff)olursa olsun kaçınmalı mıyım ?

echo $(stuff)Ne yaptığınızı biliyorsanız , kullanmak isteyebileceğiniz bir neden var . Aynı nedenle, echo $(stuff)ne yaptığınızı gerçekten bilmiyorsanız kaçınmalısınız .

Mesele tam olarak eşdeğerdir stuffve echo $(stuff)değildir. Sonuncusu, çıktısında stuffvarsayılan değer olan split + glob operatörünü çağırmaktır $IFS. Komut ikamesini çift tırnak içine almak bunu önler. Komut ikame işleminden tekli alıntı yapmak artık bir komut ikame işlemi olmamasını sağlıyor.

Bölünme söz konusu olduğunda bunu gözlemlemek için şu komutları çalıştırın:

echo "a       b"
echo $(echo "a       b")
echo "$(echo "a       b")"  # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a       b")'

Ve globbing için:

echo "/*"
echo $(echo "/*")
echo "$(echo "/*")"  # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'

Gördüğünüz gibi echo "$(stuff)"eşdeğer (-ish *) ila stuff. Kullanabilirsin ama işleri bu şekilde karmaşıklaştırmanın amacı ne?

Öte yandan eğer istediğiniz çıkışı stuffo zaman globbing bölme + geçmesi olabilir bulmak echo $(stuff)yararlıdır. Yine de bilinçli kararın olmalı.

Değerlendirilmesi gereken (bölücü, globbing ve daha fazlasını içeren) ve kabuk tarafından çalıştırılan çıktı üreten komutlar vardır, bu eval "$(stuff)"da bir olasılıktır ( bu cevaba bakınız ). Ben önce ek bölme + globbing geçmesi çıkışını gereken bir komut görmemiştim baskılı . Kasten kullanmak echo $(stuff)çok nadir görünüyor.


Ne hakkında var=$(stuff); echo "$var"?

İyi bir nokta. Bu pasajı:

var=$(stuff)
echo "$var"

echo "$(stuff)"eşdeğerde (-ish *) ile eşdeğer olmalıdır stuff. Kodun tamamıysa, stuffbunun yerine çalıştırın .

Ancak, stuffbu yaklaşımı bir kereden fazla çıktı kullanmanız gerekiyorsa,

var=$(stuff)
foo "$var"
bar "$var"

genellikle daha iyidir

foo "$(stuff)"
bar "$(stuff)"

Kodunuzda olsa ve kodunuzu girseniz bile foo, bu şekilde tutmak daha iyi olabilir. Düşünülmesi gereken şeyler:echoecho "$var"

  1. Bir var=$(stuff) stuffkez çalışır; komut hızlı olsa bile, aynı çıktının iki kez hesaplanmasından kaçınmak doğru şeydir. Belki de stuffstdout'a yazmaktan başka efektler olabilir (örneğin, geçici bir dosya oluşturmak, bir servisi başlatmak, sanal bir makineyi başlatmak, uzak bir sunucuyu bildirmek), bu yüzden birden fazla kez çalıştırmak istemezsiniz.
  2. Eğer stuffzaman bağlı veya biraz rasgele çıktı üretir, aralarından tutarsız sonuçlar alabilirsiniz foo "$(stuff)"ve bar "$(stuff)". Sonra var=$(stuff)değeri $varve sabittir emin olabilirsiniz foo "$var"ve bar "$var"aynı komut satırı argümanı olsun.

Bazı durumlarda yerine foo "$var"kullanmak (gereğini) isteyebilirsiniz foo $var, özellikle stuffüretir birden argümanlarını foo(Kabuk desteklediği bunu eğer bir dizi değişkeni daha iyi olabilir). Yine, ne yaptığını biliyorsun. O gelince echoarasındaki fark echo $varve echo "$var"aralarında aynıdır echo $(stuff)ve echo "$(stuff)".


* Eşdeğer ( -ish )?

echo "$(stuff)"Eşittir dedim (-ish) stuff. Tam olarak eşdeğer olmayan en az iki sorun var:

  1. $(stuff)stuffBir alt kabukta çalışır , bu yüzden echo "$(stuff)"eşdeğer (-ish) demek daha iyidir (stuff). Alt kabuklarında çalıştırdıkları kabuğu etkileyen komutlar, ana kabuğu etkilemez.

    Bu örnekte stuffolduğu a=1; echo "$a":

    a=0
    echo "$(a=1; echo "$a")"      # echo "$(stuff)"
    echo "$a"
    

    Şununla karşılaştır

    a=0
    a=1; echo "$a"                # stuff
    echo "$a"
    

    Ve birlikte

    a=0
    (a=1; echo "$a")              # (stuff)
    echo "$a"
    

    Başka bir örnek, stuffolmakla başlayın cd /; pwd:

    cd /bin
    echo "$(cd /; pwd)"           # echo "$(stuff)"
    pwd
    

    ve test stuffve (stuff)sürümleri.

  2. echokontrolsüz verileri görüntülemek için iyi bir araç değildir . Bu echo "$var"konuştuğumuz şey olmalıydı printf '%s\n' "$var". Ancak sorudan bahsettiği echove en olası çözüm echoilk etapta kullanmak olmadığı için, printfşimdiye kadar anlatmamaya karar verdim .

  3. stuffveya (stuff)stdout ve stderr çıkışını serpiştirecek, echo $(stuff)tüm (stderr çıktısı olan) stderr çıktısını stuffve yalnızca sonra echo(son çalıştırılan) sindirilen stdout çıktısını yazdıracaktır .

  4. $(…)izleyen her yeni satırdan sıyrılır ve sonra echotekrar eklenir. Bu yüzden echo "$(printf %s 'a')" | xxdfarklı çıktı verir printf %s 'a' | xxd.

  5. Bazı komutlar ( lsörneğin) standart çıktının konsol olup olmamasına bağlı olarak farklılık gösterir; bu yüzden ls | cataynı değil ls. Benzer şekilde echo $(ls)farklı çalışacaktır ls.

    lsGenel bir durumda , bu diğer davranışı zorlamanız gerekiyorsa , bir kenara koymak , o stuff | catzaman daha iyidir echo $(ls)veya echo "$(ls)"burada belirtilen diğer tüm sorunları tetiklememektedir.

  6. Muhtemelen farklı çıkış statüsü (bu wiki cevabının eksiksizliği için bahsedilmiştir; ayrıntılar için krediyi hak eden başka bir cevaba bakınız ).


5
Bir fark daha: stuffyol araya girer stdoutve stderrçıktı alırken echo `stuff` , tüm stderrçıktıyı stuffve yalnızca çıktıyı yazdıracaktır stdout.
Ruslan,

3
Ve bunun için başka bir örnek: bash betiğinde çok fazla tırnak olmayabilir. Açıkça yayılma ve genişleme istemiyormuş echo $(stuff)gibi echo "$(stuff)"sizi daha fazla sıkıntıya sokabilir .
rexkogitans

2
Bir hafif fark daha: $(…)takip eden her yeni satırdan sıyrılır ve sonra echogeri eklenir. Bu yüzden echo "$(printf %s 'a')" | xxdfarklı çıktı verir printf %s 'a' | xxd.
derobert

1
Bazı lsçıktıların ( örneğin) standart çıkışın konsol olup olmamasına bağlı olarak farklı çalıştığını unutmamalısınız ; bu yüzden ls | cataynı değil ls. Ve elbette echo $(ls)farklı şekilde çalışacak ls.
Martin Rosenau

@Ruslan Cevap şimdi topluluk wiki. Faydalı yorumunuzu wiki'ye ekledim. Teşekkür ederim.
Kamil Maciorowski

5

Başka bir fark: Alt kabuk çıkış kodu kaybolur, echobunun yerine çıkış kodu alınır.

> stuff() { return 1
}
> stuff; echo $?
1
> echo $(stuff); echo $?
0

3
Süper Kullanıcıya Hoşgeldiniz! Gösterdiğiniz farkı açıklayabilir misiniz? Thanks
bertieb

4
Bunun $?, dönüş kodunun, basılanın yerine echo(for echo $(stuff)) dönüş kodu olacağına inanıyorum . Fonksiyonu (çıkış kodu), ama ne olursa olsun dönüş kodu "0" setleri o . Yine de cevapta olmalı. returnstuffstuffreturn 1echostuff
Ismael Miguel,

alt
kabuktan
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.