Variabel dengan tanda kutip “$ ()”

12

Saya menulis skrip ini:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Saya seorang pemula yang mencoba belajar dengan cepat dan saya mendapat pertanyaan tentang tanda kutip variabel .

Letakkan variabel antara $ (), saya mengerti, tetapi mengapa tanda kutip dibutuhkan bahkan dalam ifpernyataan? Apakah itu untuk membuat perintah bersarang?

Shankhara
sumber
5
Catatan yang while [ true ]memang menghasilkan loop tak terbatas, tetapi mungkin bukan karena alasan yang Anda pikirkan; while [ false ] juga menghasilkan loop tak terbatas, karena dengan argumen tunggal, [ ... ]berhasil jika argumen itu adalah string yang tidak kosong. while trueakan benar - benar menjalankan perintah bernama true(yang selalu berhasil).
chepner
4
Anda tidak memasukkan variabel ke dalamnya $(). Anda menaruh perintah di dalam $().
Wildcard

Jawaban:

22

Tanda kutip mencegah "pemisahan kata". Yaitu: memecah variabel menjadi beberapa item pada karakter spasi putih (atau lebih tepatnya, pada spasi, tab, dan baris baru seperti yang didefinisikan dalam nilai $IFSvariabel shell default ).

Sebagai contoh,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Di sini kita mendefinisikan howmanyfungsi yang hanya memberi tahu kita berapa banyak parameter posisi yang diberikan. Seperti yang Anda lihat, ada dua item yang diteruskan ke variabel, dan dengan tanda kutip teks dalam variabel diperlakukan sebagai satu unit.

Ini penting untuk memberikan informasi yang akurat. Misalnya, jika variabel berisi path ke file, dan nama file berisi spasi di mana saja di path, perintah yang Anda coba jalankan mungkin gagal atau memberikan hasil yang tidak akurat. Jika kami mencoba membuat file dengan $varvariabel,touch $var akan membuat dua file, tetapi touch "$var"hanya satu.

Sama halnya dengan Anda [ "$currentoutput" != "$lastoutput" ]. Tes khusus ini melakukan perbandingan pada dua string. Ketika tes berjalan, [perintah harus melihat 3 argumen - string teks, !=operator, dan string teks lainnya. Mempertahankan tanda kutip ganda mencegah pemisahan kata, dan [perintah melihat dengan tepat 3 argumen itu. Sekarang apa yang terjadi jika variabel tidak dikutip?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Di sini, pemisahan kata terjadi, dan sebagai gantinya [melihat dua string hellodan worlddiikuti oleh!= , diikuti oleh dua string lainnya hi world. Poin utamanya adalah bahwa tanpa tanda kutip ganda, isi variabel dipahami sebagai unit yang terpisah dan bukan satu item keseluruhan.

Menetapkan substitusi perintah tidak memerlukan tanda kutip ganda seperti pada

var=$( df )

di mana Anda memiliki dfoutput perintah disimpan var. Namun, merupakan kebiasaan yang baik untuk selalu menggandakan variabel kutipan dan memerintahkan substitusi $(...)kecuali Anda memang menginginkan output diperlakukan sebagai item terpisah.


Di samping catatan,

while [ true ]

bagian bisa

while true

[adalah perintah yang mengevaluasi argumennya, dan [ whatever ]selalu benar terlepas dari apa yang ada di dalamnya. Sebaliknya, while truegunakan perintah trueyang selalu mengembalikan status keluar sukses (dan itulah yang whiledibutuhkan loop). Perbedaannya sedikit lebih jelas dan sedikit pengujian yang dilakukan. Atau, Anda juga bisa menggunakan :bukannyatrue

Kutipan ganda echo "" date and Timesebagian mungkin dapat dihapus. Mereka hanya memasukkan string kosong dan menambahkan ruang ekstra ke output. Jika itu diinginkan, jangan ragu untuk menyimpannya di sana, tetapi tidak ada nilai fungsional tertentu dalam kasus ini.

lsusb >> test.log

Bagian ini mungkin bisa diganti dengan echo "$currentoutput" >> test.log. Tidak ada alasan untuk menjalankan lsusblagi setelah dijalankan currentoutput=$(lsusb). Dalam kasus di mana trailing baris baru harus dipertahankan dalam output - orang bisa melihat nilai dalam menjalankan perintah beberapa kali, tetapi dalam kasus lsusbtidak perlu untuk itu. Semakin sedikit perintah eksternal yang Anda panggil, semakin baik, karena setiap panggilan ke perintah non-built-in menimbulkan biaya dalam CPU, penggunaan memori, dan waktu eksekusi (meskipun perintah mungkin sudah dimuat sebelumnya dari memori).


Lihat juga:

Sergiy Kolodyazhnyy
sumber
1
Jawaban yang bagus, jika Anda belum melakukannya, Anda harus menulis buku tentang bash. Tetapi pada bagian terakhir tidak seharusnya echo "$currentoutput" >> test.log lebih baik printf '%s\n' "$currentoutput" >> test.log ?
pLumo
2
@RoVo Ya, printfharus dipilih untuk portabilitas. Karena kami menggunakan skrip khusus-bash di sini, itu dapat dimaafkan untuk digunakan echo. Tetapi pengamatan Anda sangat benar
Sergiy Kolodyazhnyy
1
@SergiyKolodyazhnyy, ... Saya tidak yakin itu echosepenuhnya dapat diandalkan bahkan ketika shell dikenal sebagai bash (dan nilai xpg_echodan posixbendera diketahui); jika nilai Anda bisa -natau -e, itu bisa hilang alih-alih dicetak.
Charles Duffy
4

Dalam currentoutput="$(lsusb)"lsusb bukan variabel, itu adalah perintah. Apa yang dilakukan pernyataan ini, menjalankan lsusbperintah dan memberikan outputnya ke currentoutputvariabel.

Sintaks yang lebih lama untuk ini adalah

currentoutput=`lsusb`

Anda dapat menemukannya di banyak contoh dan skrip

Untuk menjawab bagian lain dari pertanyaan Anda, if [ ]bagaimana sintaks untuk ifdidefinisikan dalam bash. Lihat lainnya di https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html

marosg
sumber
3
Saya pikir penting untuk mengatakan bahwa [ ]itu sebenarnya testperintah. Anda dapat menggunakan ifpernyataan dengan perintah lain juga, karena itu bergantung pada tes 0 atau tidak nol dari kode keluar mereka.
Arronical
... memang, ini jauh lebih baik untuk dijalankan if grep -qe "somestring" filedaripada dijalankan grep -qe "somestring" file; if [ $? = 0 ]; then ..., sehingga klaim yang if [ ...merupakan bagian dari definisi ifsintaksis tidak hanya menyesatkan tetapi mengarah pada praktik buruk.
Charles Duffy
4

Berikut ini menjalankan perintah eksternal commanddan mengembalikan hasilnya.

"$(command)"

Tanpa tanda kurung / tanda kurung, ini akan mencari variabel alih-alih menjalankan perintah:

"$variable"

Adapun perbedaan antara $variabledan "$variable", ini menjadi relevan ketika $variableberisi spasi. Saat menggunakan "$variable", seluruh konten variabel akan dimasukkan ke dalam string tunggal bahkan jika konten menyertakan spasi. Saat menggunakan $variablekonten variabel dapat diperluas ke daftar argumen beberapa argumen.

thomasrutter
sumber
HI @ thomasrutter, maaf, maksud saya tanda kutip ... Saya mengedit komentar saya sekarang!
Shankhara
0

Untuk menjadi pelawan - bash merekomendasikan Anda menggunakan [[ ... ]]over the [ ... ]construct untuk menghindari keharusan mengutip variabel dalam tes dan masalah terkait pemisahan kata yang ditunjukkan oleh yang lain.

[disediakan di bash untuk kompatibilitas POSIX dengan skrip yang dimaksudkan untuk berjalan di bawah #!/bin/shatau yang sedang di-port ke bash - untuk sebagian besar, Anda harus menghindarinya [[.

misalnya

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal
shalomb
sumber
bashtidak membuat rekomendasi seperti itu; ini menyediakan [[ ... ]] mana yang bisa lebih nyaman dan memiliki beberapa fungsi yang [ ... ]tidak, tetapi tidak ada masalah dengan menggunakan dengan [ ... ] benar .
chepner
@ chepner - Mungkin saya harus lebih jelas di sini. Tentu itu bukan rekomendasi eksplisit di bash manpage tetapi diberikan [[tidak semuanya [berjalan dan masih banyak kali membuat [orang tersandung dan kemudian mencoba dan retrofit fitur [[kembali ke [hanya untuk gagal dan meninggalkan bug berserakan - itu adil untuk mengatakan itu adalah rekomendasi komunitas sejauh panduan gaya bash paling relevan akan menyarankan tidak pernah digunakan[ .
shalomb