Saya menggunakan salah satu dari yang berikut ini
echo $(stuff)
echo `stuff`
(Di mana stuff
misalnya pwd
atau date
atau sesuatu yang lebih rumit).
Kemudian saya diberitahu bahwa sintaks ini salah, praktik buruk, tidak elegan, berlebihan, berlebihan, terlalu rumit, pemrograman pemujaan kargo, noobish, naif, dll.
Tetapi perintah itu berhasil, jadi apa sebenarnya yang salah dengannya?
shell-script
sh
echo
Kamil Maciorowski
sumber
sumber
echo $(echo $(echo $(echo$(echo $(stuff)))))
juga tidak berfungsi, dan Anda mungkin masih tidak akan menggunakannya, kan? ;-)stuff
. : DJawaban:
tl; dr
Sebuah tunggal
stuff
akan paling mungkin bekerja untuk Anda.Jawaban penuh
Apa yang terjadi
Ketika Anda berlari
foo $(stuff)
, inilah yang terjadi:stuff
berjalan;$(stuff)
dalam doafoo
;foo
berjalan, argumen baris perintahnya jelas tergantung pada apa yangstuff
dikembalikan.Ini
$(…)
mekanisme yang disebut "perintah substitusi". Dalam kasus Anda, perintah utama adalahecho
yang pada dasarnya mencetak argumen baris perintahnya ke stdout. Jadi apapun yangstuff
mencoba untuk mencetak ke stdout ditangkap, diteruskan keecho
dan dicetak ke stdout olehecho
.Jika Anda ingin keluaran
stuff
dicetak ke stdout, jalankan solnyastuff
.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 menghindariecho $(stuff)
jika Anda tidak benar-benar tahu apa yang Anda lakukan.Intinya adalah
stuff
danecho $(stuff)
tidak persis sama. Yang terakhir berarti memanggil operator split + glob pada outputstuff
dengan 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:
Dan untuk globbing:
Seperti yang Anda lihat
echo "$(stuff)"
adalah setara (-ish *) untukstuff
. Anda bisa menggunakannya tetapi apa gunanya menyulitkan dengan cara ini?Di sisi lain, jika Anda ingin output
stuff
mengalami splitting + globbing maka Anda mungkin menemukanecho $(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 disengajaecho $(stuff)
tampaknya sangat jarang.Bagaimana dengan
var=$(stuff); echo "$var"
?Poin bagus. Cuplikan ini:
harus setara dengan
echo "$(stuff)"
setara (-ish *) untukstuff
. Jika seluruh kode, jalankanstuff
saja.Namun, jika Anda perlu menggunakan output
stuff
lebih dari satu kali maka pendekatan inibiasanya lebih baik daripada
Bahkan jika
foo
adaecho
dan Anda mendapatkanecho "$var"
dalam kode Anda, mungkin lebih baik untuk tetap seperti ini. Hal yang perlu dipertimbangkan:var=$(stuff)
stuff
sekali berlari; bahkan jika perintahnya cepat, menghindari penghitungan output yang sama dua kali adalah hal yang benar. Atau mungkinstuff
memiliki 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.stuff
menghasilkan keluaran tergantung waktu atau agak acak, Anda mungkin mendapatkan hasil yang tidak konsisten darifoo "$(stuff)"
danbar "$(stuff)"
. Setelahvar=$(stuff)
nilai$var
diperbaiki dan Anda dapat yakinfoo "$var"
danbar "$var"
mendapatkan argumen baris perintah yang identik.Dalam beberapa kasus, alih-alih
foo "$var"
Anda mungkin ingin (perlu) untuk menggunakanfoo $var
, terutama jikastuff
menghasilkan beberapa argumen untukfoo
(variabel array mungkin lebih baik jika shell Anda mendukungnya). Sekali lagi, ketahuilah apa yang Anda lakukan. Ketika datang keecho
perbedaan antaraecho $var
danecho "$var"
sama dengan antaraecho $(stuff)
danecho "$(stuff)"
.* Setara ( -ish )?
Saya katakan
echo "$(stuff)"
setara (-ish) untukstuff
. Setidaknya ada dua masalah yang membuatnya tidak persis sama:$(stuff)
berjalanstuff
dalam subkulit, jadi lebih baik untuk mengatakanecho "$(stuff)"
itu setara (-ish) untuk(stuff)
. Perintah yang memengaruhi shell yang dijalankan, jika dalam subkulit, tidak memengaruhi shell utama.Dalam contoh ini
stuff
adalaha=1; echo "$a"
:Bandingkan dengan
dan dengan
Contoh lain, mulailah dengan
stuff
menjadicd /; pwd
:dan tes
stuff
dan(stuff)
versi.echo
bukan alat yang baik untuk menampilkan data yang tidak terkontrol . Ini yangecho "$var"
kita bicarakan seharusnyaprintf '%s\n' "$var"
. Tetapi karena pertanyaan itu menyebutkanecho
dan karena solusi yang paling mungkin adalah tidak menggunakanecho
sejak awal, saya memutuskan untuk tidak memperkenalkannyaprintf
sampai sekarang.stuff
atau(stuff)
akan interleave output stdout dan stderr, sementaraecho $(stuff)
akan mencetak semua output stderr daristuff
(yang berjalan pertama), dan hanya kemudian output stdout dicerna olehecho
(yang berjalan terakhir).$(…)
menghapus semua baris baru yang tertinggal dan kemudianecho
menambahkannya kembali. Jadiecho "$(printf %s 'a')" | xxd
memberikan output yang berbeda dariprintf %s 'a' | xxd
.Beberapa perintah (
ls
misalnya) bekerja secara berbeda tergantung apakah output standar adalah konsol atau tidak; begituls | cat
juga tidak samals
. Demikian pulaecho $(ls)
akan bekerja secara berbeda darils
.Mengesampingkan
ls
, dalam kasus umum jika Anda harus memaksakan perilaku lain ini makastuff | cat
lebih baik daripadaecho $(ls)
atauecho "$(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).
sumber
stuff
caranya akan interleavestdout
danstderr
output, sementaraecho `stuff`
akan mencetak semuastderr
output daristuff
, dan hanya kemudianstdout
output.echo $(stuff)
dapat menempatkan Anda ke masalah yang jauh lebih banyak seolah-echo "$(stuff)"
olah Anda tidak ingin globbing dan ekspansi secara eksplisit.$(…)
menghapus semua baris baru dan kemudianecho
menambahkannya kembali. Jadiecho "$(printf %s 'a')" | xxd
memberikan output yang berbeda dariprintf %s 'a' | xxd
.ls
misalnya) bekerja secara berbeda tergantung apakah output standar adalah konsol atau tidak; begituls | cat
juga tidak samals
. Dan tentu sajaecho $(ls)
akan bekerja berbeda darils
.Perbedaan lain: Kode keluar sub-shell hilang, jadi kode keluar dari
echo
diambil sebagai gantinya.sumber
$?
akan menjadi kode pengembalianecho
(untukecho $(stuff)
) alih-alih yangreturn
diedit olehstuff
. Thestuff
Fungsireturn 1
(kode keluar), tapiecho
set ke "0" terlepas dari kode kembalistuff
. Masih harus dijawab.