tl; dr
Sebuah tunggal stuffakan paling mungkin bekerja untuk Anda.
Jawaban penuh
Apa yang terjadi
Ketika Anda berlari foo $(stuff), inilah yang terjadi:
stuff berjalan;
- outputnya (stdout), bukannya dicetak, menggantikan
$(stuff)dalam doa foo;
- kemudian
fooberjalan, argumen baris perintahnya jelas tergantung pada apa yang stuffdikembalikan.
Ini $(…)mekanisme yang disebut "perintah substitusi". Dalam kasus Anda, perintah utama adalah echoyang pada dasarnya mencetak argumen baris perintahnya ke stdout. Jadi apapun yang stuffmencoba untuk mencetak ke stdout ditangkap, diteruskan ke echodan dicetak ke stdout oleh echo.
Jika Anda ingin keluaran stuffdicetak ke stdout, jalankan solnya stuff.
The `…`sintaks melayani tujuan yang sama sebagai $(…)(dengan nama yang sama: "perintah substitusi"), ada beberapa perbedaan, jadi Anda tidak bisa begitu saja interchange mereka. Lihat FAQ ini dan pertanyaan ini .
Haruskah saya menghindari echo $(stuff)apa pun?
Ada alasan yang mungkin ingin Anda gunakan echo $(stuff)jika Anda tahu apa yang Anda lakukan. Untuk alasan yang sama Anda harus menghindari echo $(stuff)jika Anda tidak benar-benar tahu apa yang Anda lakukan.
Intinya adalah stuffdan echo $(stuff)tidak persis sama. Yang terakhir berarti memanggil operator split + glob pada output stuffdengan nilai default $IFS. Pengutipan perintah berlapis ganda mencegah hal ini. Mengutip substitusi perintah tunggal membuatnya tidak lagi menjadi substitusi perintah.
Untuk mengamati ini ketika melakukan splitting, jalankan perintah berikut:
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")'
Dan untuk globbing:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Seperti yang Anda lihat echo "$(stuff)"adalah setara (-ish *) untuk stuff. Anda bisa menggunakannya tetapi apa gunanya menyulitkan dengan cara ini?
Di sisi lain, jika Anda ingin output stuffmengalami splitting + globbing maka Anda mungkin menemukan echo $(stuff)berguna. Itu harus menjadi keputusan sadar Anda.
Ada perintah yang menghasilkan keluaran yang harus dievaluasi (yang meliputi pemisahan, globbing dan banyak lagi) dan dijalankan oleh shell, jadi eval "$(stuff)"ada kemungkinan (lihat jawaban ini ). Saya belum pernah melihat perintah yang membutuhkan outputnya untuk menjalani splitting + globbing tambahan sebelum dicetak . Penggunaan yang disengaja echo $(stuff)tampaknya sangat jarang.
Bagaimana dengan var=$(stuff); echo "$var"?
Poin bagus. Cuplikan ini:
var=$(stuff)
echo "$var"
harus setara dengan echo "$(stuff)"setara (-ish *) untuk stuff. Jika seluruh kode, jalankan stuffsaja.
Namun, jika Anda perlu menggunakan output stufflebih dari satu kali maka pendekatan ini
var=$(stuff)
foo "$var"
bar "$var"
biasanya lebih baik daripada
foo "$(stuff)"
bar "$(stuff)"
Bahkan jika fooada echodan Anda mendapatkan echo "$var"dalam kode Anda, mungkin lebih baik untuk tetap seperti ini. Hal yang perlu dipertimbangkan:
- Dengan
var=$(stuff) stuffsekali berlari; bahkan jika perintahnya cepat, menghindari penghitungan output yang sama dua kali adalah hal yang benar. Atau mungkin stuffmemiliki efek selain menulis ke stdout (misalnya membuat file sementara, memulai layanan, memulai mesin virtual, memberi tahu server jauh), jadi Anda tidak ingin menjalankannya berkali-kali.
- Jika
stuffmenghasilkan keluaran tergantung waktu atau agak acak, Anda mungkin mendapatkan hasil yang tidak konsisten dari foo "$(stuff)"dan bar "$(stuff)". Setelah var=$(stuff)nilai $vardiperbaiki dan Anda dapat yakin foo "$var"dan bar "$var"mendapatkan argumen baris perintah yang identik.
Dalam beberapa kasus, alih-alih foo "$var"Anda mungkin ingin (perlu) untuk menggunakan foo $var, terutama jika stuffmenghasilkan beberapa argumen untuk foo(variabel array mungkin lebih baik jika shell Anda mendukungnya). Sekali lagi, ketahuilah apa yang Anda lakukan. Ketika datang ke echoperbedaan antara echo $vardan echo "$var"sama dengan antara echo $(stuff)dan echo "$(stuff)".
* Setara ( -ish )?
Saya katakan echo "$(stuff)"setara (-ish) untuk stuff. Setidaknya ada dua masalah yang membuatnya tidak persis sama:
$(stuff)berjalan stuffdalam subkulit, jadi lebih baik untuk mengatakan echo "$(stuff)"itu setara (-ish) untuk (stuff). Perintah yang memengaruhi shell yang dijalankan, jika dalam subkulit, tidak memengaruhi shell utama.
Dalam contoh ini stuffadalah a=1; echo "$a":
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
Bandingkan dengan
a=0
a=1; echo "$a" # stuff
echo "$a"
dan dengan
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Contoh lain, mulailah dengan stuffmenjadi cd /; pwd:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
dan tes stuffdan (stuff)versi.
echobukan alat yang baik untuk menampilkan data yang tidak terkontrol . Ini yang echo "$var"kita bicarakan seharusnya printf '%s\n' "$var". Tetapi karena pertanyaan itu menyebutkan echodan karena solusi yang paling mungkin adalah tidak menggunakan echosejak awal, saya memutuskan untuk tidak memperkenalkannya printfsampai sekarang.
stuffatau (stuff)akan interleave output stdout dan stderr, sementara echo $(stuff)akan mencetak semua output stderr dari stuff(yang berjalan pertama), dan hanya kemudian output stdout dicerna oleh echo(yang berjalan terakhir).
$(…)menghapus semua baris baru yang tertinggal dan kemudian echomenambahkannya kembali. Jadi echo "$(printf %s 'a')" | xxdmemberikan output yang berbeda dari printf %s 'a' | xxd.
Beberapa perintah ( lsmisalnya) bekerja secara berbeda tergantung apakah output standar adalah konsol atau tidak; begitu ls | catjuga tidak sama ls. Demikian pula echo $(ls)akan bekerja secara berbeda dari ls.
Mengesampingkan ls, dalam kasus umum jika Anda harus memaksakan perilaku lain ini maka stuff | catlebih baik daripada echo $(ls)atau echo "$(ls)"karena itu tidak memicu semua masalah lain yang disebutkan di sini.
Status keluar yang sangat berbeda (disebutkan untuk kelengkapan jawaban wiki ini; untuk perincian lihat jawaban lain yang layak mendapat pujian).