Saya membaca bahwa saya harus mengutip variabel dalam bash, misalnya "$ foo", bukan $ foo. Namun, saat menulis skrip, saya menemukan sebuah kasus di mana ia bekerja tanpa tanda kutip tetapi tidak dengan mereka:
wget_options='--mirror --no-host-directories'
local_root="$1" # ./testdir recieved from command line
remote_root="$2" # ftp://XXX recieved from command line
relative_path="$3" # /XXX received from command line
Yang ini berfungsi:
wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"
Yang ini tidak (perhatikan tanda kutip ganda aroung $ wget_options):
wget "$wget_options" --directory_prefix="$local_root" "$remote_root$relative_path"
Apa alasannya?
Apakah baris pertama versi yang bagus; atau haruskah saya curiga ada kesalahan tersembunyi di suatu tempat yang menyebabkan perilaku ini?
Secara umum, di mana saya menemukan dokumentasi yang bagus untuk memahami bagaimana bash dan penawarannya bekerja? Selama menulis skrip ini, saya merasa mulai bekerja dengan dasar coba-coba alih-alih memahami aturan.
wget
tidak tahu apa--mirror --no-host-directories
artinya (sebagai satu argumen), tetapi itu menanganinya ketika itu dibagi menjadi dua argumen. Sangat sedikit program yang memperlakukan spasi dan kutipan secara khusus begitu mereka berada di dalam vektor argumen. Masalahnya adalah bahwabash
, dan kerang lainnya, dimaksudkan untuk menjadi>bash
dilakukan, sehingga Anda dapat membayangkan bahwa$a
persis sama dengan langsung menulis kontennya. Sekarang masalahnya sudah jelas:a="-a -b"; cmd "$a"
diperluascmd "-a -b"
, tetapicmd
mungkin tidak tahu apa artinya itu.cmd $a
mengembang untukcmd -a -b
, yang mungkin tidak bekerja.Jawaban:
Pada dasarnya, Anda harus menggandakan ekspansi variabel penawaran untuk melindungi mereka dari pemisahan kata (dan pembuatan nama file). Namun, dalam contoh Anda,
pemisahan kata adalah persis apa yang Anda inginkan .
Dengan
"$wget_options"
(dikutip),wget
tidak tahu apa yang harus dilakukan dengan argumen tunggal--mirror --no-host-directories
dan mengeluhUntuk
wget
melihat dua opsi--mirror
dan--no-host-directories
sebagai terpisah, pemisahan kata harus terjadi.Ada cara yang lebih kuat untuk melakukan ini. Jika Anda menggunakan
bash
atau shell lain yang menggunakan array sepertibash
do, lihat jawaban glenn jackman . Jawaban Gilles juga menjelaskan solusi alternatif untuk cangkang yang lebih jelas seperti standar/bin/sh
. Keduanya pada dasarnya menyimpan setiap opsi sebagai elemen terpisah dalam sebuah array.Pertanyaan terkait dengan jawaban yang baik: Mengapa skrip shell saya tercekik di spasi putih atau karakter khusus lainnya?
Ekspansi variabel kuotasi ganda adalah aturan praktis yang baik. Lakukan itu . Maka waspadai beberapa kasus di mana Anda seharusnya tidak melakukan itu. Ini akan hadir sendiri kepada Anda melalui pesan diagnostik, seperti pesan kesalahan di atas.
Ada juga beberapa kasus di mana Anda tidak perlu mengutip ekspansi variabel. Tapi bagaimanapun, lebih mudah untuk terus menggunakan tanda kutip ganda karena tidak ada banyak perbedaan. Salah satu kasusnya adalah
Yang lain adalah
sumber
$IFS
berisi nilai yang tepat. Di sini Anda perlu membagi ruang dan teks terjadi tidak mengandung tab atau baris baru, sehingga nilai default$IFS
akan dilakukan, tetapi jika kode itu akan digunakan dalam fungsi yang dapat dipanggil dalam konteks di mana$IFS
bisa dimodifikasi , Anda ingin mengatur$IFS
sebelumnya (dan mungkin mengembalikannya sesudahnya atau menggunakan cakupan lokal untuk itu jika sisa kode diasumsikan tidak dimodifikasi$IFS
)Cara paling kuat untuk mengkodekan itu adalah dengan menggunakan array:
sumber
cp
danrsync
akan melakukan hal-hal yang tidak terduga jika perintah Anda diperluas ke sesuatu sepertirsync '' rest of parameters
. Ini bagus untuk membangun perintah sepotong demi sepotong secara kondisional dan kemudian hanya menjalankannya sekali di satu tempat.Anda mencoba menyimpan daftar string dalam variabel string. Itu tidak cocok. Tidak peduli bagaimana Anda mengakses variabel, ada yang rusak.
wget_options='--mirror --no-host-directories'
set variabelwget_options
ke string yang berisi spasi. Pada titik ini, tidak ada cara untuk mengetahui apakah ruang tersebut seharusnya menjadi bagian dari opsi, atau pemisah antara opsi.Saat Anda mengakses variabel dengan substitusi yang dikutip
wget "$wget_options"
, nilai variabel digunakan sebagai string. Ini berarti bahwa parameter itu dilewatkan sebagai parameter tunggalwget
, jadi ini adalah opsi tunggal. Ini pecah dalam kasus Anda karena Anda bermaksud itu berarti beberapa opsi.Saat Anda menggunakan subtitusi yang tidak dikutip
wget $wget_options
, nilai variabel string mengalami proses ekspansi yang dijuluki "split + glob":$IFS
variabel). Ini menghasilkan daftar string menengah.Ini terjadi untuk bekerja dalam contoh Anda, karena proses pemisahan mengubah spasi menjadi pemisah, tetapi tidak berfungsi secara umum karena opsi dapat berisi spasi dan karakter wildcard.
Di ksh, bash, yash dan zsh, Anda bisa menggunakan variabel array. Array dalam terminologi shell adalah daftar string, sehingga tidak ada kehilangan informasi. Untuk membuat variabel array, letakkan tanda kurung di sekitar elemen array saat menetapkan nilai ke variabel. Untuk mengakses semua elemen array, gunakan - ini adalah generalisasi dari , yang membentuk daftar dari elemen-elemen array. Perhatikan bahwa Anda memerlukan tanda kutip ganda di sini juga, jika tidak setiap elemen mengalami split + glob.
"${VARIABLE[@]}"
"$@"
Di sh polos, tidak ada variabel array. Jika Anda tidak keberatan kehilangan argumen posisi, Anda dapat menggunakannya untuk menyimpan satu daftar string.
Untuk informasi lebih lanjut, lihat Mengapa skrip shell saya tercekik di spasi putih atau karakter khusus lainnya?
sumber
(set -- ...; exec wget "$@" ...)
.