Saya telah membaca bahwa Anda memerlukan tanda kutip ganda untuk memperluas variabel, misalnya
if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi
akan bekerja seperti yang diharapkan, sementara
if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi
akan selalu mengatakan $test ok
bahkan jika $test
itu nol.
tapi mengapa kita tidak perlu mengutip echo $test
?
echo
, spasi tambahan dan baris baru akan dihapus.Jawaban:
Anda selalu memerlukan tanda kutip di sekitar variabel dalam semua konteks daftar , di mana-mana variabel tersebut dapat diperluas ke beberapa nilai kecuali Anda memang menginginkan 3 efek samping dari meninggalkan variabel yang tidak dikutip.
Daftar konteks termasuk argumen untuk perintah sederhana seperti
[
atauecho
, yangfor i in <here>
, tugas untuk array ... Ada konteks lain di mana variabel juga perlu dikutip. Yang terbaik adalah selalu mengutip variabel kecuali Anda punya alasan yang sangat bagus untuk tidak melakukannya.Pikirkan tentang tidak adanya tanda kutip (dalam konteks daftar) sebagai operator split + glob .
Seolah-olah
echo $test
ituecho glob(split("$test"))
.Perilaku shell membingungkan bagi kebanyakan orang karena di sebagian besar bahasa lain, Anda menempatkan tanda kutip di sekitar string tetap, seperti
puts("foo")
, dan tidak di sekitar variabel (sepertiputs(var)
) sementara di shell itu sebaliknya: semuanya adalah string dalam shell, jadi menempatkan tanda kutip di sekitar segalanya akan merepotkan, Andaecho test
, Anda tidak perlu"echo" "test"
. Dalam shell, kutipan digunakan untuk hal lain: mencegah beberapa makna khusus dari beberapa karakter dan / atau mempengaruhi perilaku beberapa ekspansi.Di
[ -n $test ]
atauecho $test
, shell akan membagi$test
(kosong pada secara default), dan kemudian melakukan pembuatan nama file (perluas semua*
, '?' ... pola ke daftar file yang cocok), dan kemudian meneruskan daftar argumen itu ke[
atauecho
perintah .Sekali lagi, anggap itu sebagai
"[" "-n" glob(split("$test")) "]"
. Jika$test
kosong atau hanya berisi kosong (spc, tab, nl), maka operator split + glob akan mengembalikan daftar kosong, sehingga[ -n $test ]
akan menjadi"[" "-n" "]"
, yang merupakan tes untuk memeriksa apakah "-n" adalah string kosong atau tidak. Tetapi bayangkan apa yang akan terjadi jika$test
"*" atau "= foo" ...Dalam
[ -n "$test" ]
,[
disahkan empat argumen"["
,"-n"
,""
dan"]"
(tanpa tanda kutip), yang adalah apa yang kita inginkan.Apakah itu
echo
atau[
tidak ada bedanya, hanya sajaecho
menghasilkan hal yang sama apakah itu melewati argumen kosong atau tidak sama sekali.Lihat juga jawaban ini untuk pertanyaan serupa untuk perincian lebih lanjut tentang
[
perintah dan[[...]]
konstruk.sumber
@ h3rrmiller jawaban baik untuk menjelaskan mengapa Anda memerlukan tanda kutip untuk
if
(atau lebih tepatnya,[
/test
), tetapi saya benar-benar berpendapat bahwa pertanyaan Anda tidak benar.Coba perintah berikut, dan Anda akan melihat apa yang saya maksud.
Tanpa tanda kutip, substitusi variabel menyebabkan perintah kedua meluas ke:
dan beberapa ruang diciutkan menjadi satu:
Dengan tanda kutip, spasi dipertahankan.
Ini terjadi karena ketika Anda mengutip suatu parameter (apakah parameter itu diteruskan ke
echo
,test
atau perintah lain), nilai parameter itu dikirim sebagai satu nilai ke perintah. Jika Anda tidak mengutipnya, shell melakukan keajaiban normal untuk mencari spasi putih untuk menentukan di mana setiap parameter dimulai dan berakhir.Ini juga dapat diilustrasikan oleh program C (sangat sangat sederhana) berikut. Coba yang berikut ini pada baris perintah (Anda mungkin ingin melakukannya di direktori kosong agar tidak berisiko menimpa sesuatu).
lalu...
Setelah berjalan
paramtest
,$?
akan menampung jumlah parameter yang dilewati (dan nomor itu akan dicetak).sumber
Ini semua tentang bagaimana shell menginterpretasikan baris sebelum suatu program dieksekusi.
Jika baris berbunyi
echo I am $USER
, shell mengekspansi keecho I am blrfl
danecho
tidak memiliki petunjuk apakah asal teks adalah ekspansi literal atau variabel. Demikian pula, jika sebuah baris berbunyiecho I am $UNDEFINED
, shell tidak akan berkembang$UNDEFINED
menjadi apa-apa dan argumen echo akan menjadiI am
, dan itulah akhirnya. Karenaecho
berfungsi dengan baik tanpa argumen,echo $UNDEFINED
itu sepenuhnya valid.Masalah Anda dengan
if
tidak benar-benar denganif
, karenaif
hanya menjalankan program dan argumen apa pun mengikutinya dan menjalankanthen
bagian jika program keluar0
(atauelse
bagian jika ada satu dan program keluar non-0
):Ketika Anda menggunakan
if [ ... ]
untuk melakukan perbandingan, Anda tidak menggunakan primitif yang dibangun ke dalam shell. Anda benar-benar menginstruksikan shell untuk menjalankan program yang disebut[
superset sangat sedikittest(1)
yang membutuhkan argumen terakhirnya]
. Kedua program keluar0
jika kondisi pengujian keluar benar dan1
jika tidak.Alasan beberapa tes rusak ketika variabel tidak terdefinisi adalah karena
test
tidak melihat bahwa Anda menggunakan variabel. Ergo,[ $UNDEFINED -eq 2 ]
rusak karena pada saat shell selesai dengan itu, semuatest
melihat argumen-eq 2 ]
, yang bukan tes yang valid. Jika Anda melakukannya dengan sesuatu yang didefinisikan, seperti[ $DEFINED -ne 0 ]
, itu akan berhasil karena shell akan mengembangkannya menjadi tes yang valid (misalnya,0 -ne 0
).Ada perbedaan semantik antara
foo $UNDEFINED bar
, yang mengembang menjadi dua argumen (foo
danbar
) karena$UNDEFINED
sesuai dengan namanya. Bandingkan ini denganfoo "$UNDEFINED" bar
, yang mengembang menjadi tiga argumen (foo
, string kosong dan `bar). Kutipan memaksa shell untuk menafsirkannya sebagai argumen apakah ada sesuatu di antara mereka atau tidak.sumber
Tanpa tanda kutip
$test
dapat berkembang menjadi lebih dari satu kata sehingga perlu dikutip agar tidak melanggar sintaks karena setiap saklar di dalam[
perintah mengharapkan satu argumen yang merupakan apa yang dilakukan tanda kutip (membuat apa pun$test
diperluas menjadi satu argumen)Alasan Anda tidak perlu mengutip untuk memperluas variabel
echo
adalah karena tidak mengharapkan satu argumen. Ini hanya akan mencetak apa yang Anda katakan. Jadi meskipun jika$test
diperluas hingga 100 kata, echo masih akan mencetaknya.Lihatlah Bash Pitfalls
sumber
echo
?echo
. Apa yang membuat Anda berpikir sebaliknya?echo $test
dan berfungsi (ini mengeluarkan nilai $ test)Parameter kosong dihapus jika tidak dikutip:
Perintah yang dipanggil tidak melihat bahwa ada parameter kosong di baris perintah shell. Tampaknya [didefinisikan untuk mengembalikan 0 untuk -n tanpa diikuti. Kenapa
Mengutip juga membuat perbedaan untuk gema, dalam beberapa kasus:
sumber
echo
, itu shellnya. Anda akan melihat perilaku yang sama dengannyals
. Cobalahtouch '*'
beberapa saat jika Anda ingin berpetualang.:)