tl; Dr.
Bir tek stuff
olurdu muhtemelen senin için çalışıyorum.
Tam cevap
Ne oluyor
Koşarken foo $(stuff)
, olan bu:
stuff
koşar;
- çıktısı (stdout), yazdırılmak yerine
$(stuff)
, çağrısında bulunur foo
;
- sonra
foo
çalışır, komut satırı argümanları açıkça stuff
geri dönenlere bağlıdır .
Bu $(…)
mekanizmaya "komut değiştirme" denir. Senin durumunda ana komut, echo
temelde komut satırı argümanlarını stdout'a basan komuttur. Bu yüzden stuff
stdout'a yazdırmaya çalışanlar yakalanır, iletilir echo
ve 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 stuff
ve echo $(stuff)
değildir. Sonuncusu, çıktısında stuff
varsayı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ışı stuff
o 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, stuff
bunun yerine çalıştırın .
Ancak, stuff
bu 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:echo
echo "$var"
- Bir
var=$(stuff)
stuff
kez çalışır; komut hızlı olsa bile, aynı çıktının iki kez hesaplanmasından kaçınmak doğru şeydir. Belki de stuff
stdout'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.
- Eğer
stuff
zaman bağlı veya biraz rasgele çıktı üretir, aralarından tutarsız sonuçlar alabilirsiniz foo "$(stuff)"
ve bar "$(stuff)"
. Sonra var=$(stuff)
değeri $var
ve 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 echo
arasındaki fark echo $var
ve 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:
$(stuff)
stuff
Bir 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 stuff
olduğ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, stuff
olmakla başlayın cd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
ve test stuff
ve (stuff)
sürümleri.
echo
kontrolsü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 echo
ve en olası çözüm echo
ilk etapta kullanmak olmadığı için, printf
şimdiye kadar anlatmamaya karar verdim .
stuff
veya (stuff)
stdout ve stderr çıkışını serpiştirecek, echo $(stuff)
tüm (stderr çıktısı olan) stderr çıktısını stuff
ve yalnızca sonra echo
(son çalıştırılan) sindirilen stdout çıktısını yazdıracaktır .
$(…)
izleyen her yeni satırdan sıyrılır ve sonra echo
tekrar eklenir. Bu yüzden echo "$(printf %s 'a')" | xxd
farklı çıktı verir printf %s 'a' | xxd
.
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 | cat
aynı değil ls
. Benzer şekilde echo $(ls)
farklı çalışacaktır ls
.
ls
Genel bir durumda , bu diğer davranışı zorlamanız gerekiyorsa , bir kenara koymak , o stuff | cat
zaman daha iyidir echo $(ls)
veya echo "$(ls)"
burada belirtilen diğer tüm sorunları tetiklememektedir.
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 ).