Rangkaian string Bash digunakan untuk membangun daftar parameter

12

Diberikan potongan pesta ini:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

Gema menunjukkan string PARMS seperti yang diharapkan, tidak ada kesalahan yang ditampilkan, tetapi rsync diam-diam bertindak seolah-olah opsi yang ditambahkan oleh + = tidak ada. Namun, ini berfungsi seperti yang diharapkan:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Saya kira saya mengacaukan sesuatu dengan tanda kutip (selalu memiliki masalah dengan itu), tetapi tidak yakin apa dan mengapa opsi diabaikan meskipun string tampaknya telah dibangun dengan benar.

neuviemeporte
sumber
1
echo "$PARMS"dan rsync "${PARMS}"...
jasonwryan
Ini bekerja untuk saya dengan bashversi 4.2.25 tanpa perubahan apa pun.
Anthon

Jawaban:

17

Ada perbedaan antara:

PARMS+="... --exclude='.git'"

dan

... --exclude='.git'

Dalam yang pertama, tanda kutip tunggal berada di dalam tanda kutip itu sendiri, sehingga mereka secara harfiah hadir dalam teks pengganti yang diberikan rsyncsebagai argumen. rsyncmendapat argumen yang nilainya --exclude='.git'. Pada yang kedua, tanda kutip tunggal ditafsirkan oleh shell pada saat mereka ditulis, karena mereka tidak berada di dalam tanda kutip sendiri, dan rsyncbisa melihat --exclude=.git.

Dalam hal ini, Anda tidak perlu tanda kutip tunggal sama sekali - .gitadalah kata shell yang valid dengan sempurna, tanpa karakter khusus, sehingga Anda dapat menggunakannya secara harfiah dalam perintah.

Lebih baik untuk hal semacam ini, adalah array :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Ini membangun perintah Anda sebagai kata-kata yang terpisah, dengan kutipan apa pun yang Anda inginkan ditafsirkan pada saat Anda menulis baris array. "${PARMS[@]}"memperluas ke setiap entri dalam array sebagai argumen terpisah, bahkan jika argumen itu sendiri memiliki karakter atau spasi khusus di dalamnya, jadi rsynclihat apa yang Anda tulis seperti yang Anda maksudkan.

Michael Homer
sumber
bashmelakukan pemisahan kata setelah ${PARMS}diperluas. Jadi kutipan tunggal juga ditafsirkan oleh shell.
cuonglm
2
Cobalah! Aku melakukannya. Kutipan tetap, dan jika ada ruang di antara mereka adalah titik perpecahan.
Michael Homer
@Gnouc: Dari halaman pesta man: "Quote Removal: Setelah ekspansi sebelumnya, semua kejadian unquoted karakter \ , 'dan ". Yang tidak dihasilkan dari salah satu ekspansi di atas dihapus" "ekspansi di atas" termasuk Ekspansi Parameter yang melakukan perluasan ${PARMS}.
camh
Terima kasih. Jadi saya mengerti bahwa dalam hal itu menghilangkan tanda kutip tunggal di dalam tanda ganda akan berhasil tetapi demi kelengkapan - bagaimana jika saya perlu mengutip beberapa karakter khusus dan tidak ingin menggunakan pendekatan Anda yang terakhir?
neuviemeporte
Jika karakter khusus Anda bukan bagian dari IFS(umumnya, spasi), Anda tidak perlu mengutipnya. Jika ya, Anda kurang beruntung kecuali jika Anda meretas sesuatu bersama eval- ini adalah kesalahan umum pada umumnya, dan array adalah cara yang tepat untuk menghadapinya.
Michael Homer
2

Selain jawaban @Michael Homer , Anda dapat menggunakan bash fungsi eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
cuonglm
sumber
2
Anda bisa, tetapi Anda seharusnya tidak. Array ditambahkan secara khusus untuk menghindari penggunaan ini eval.
chepner