Sebagian besar jawaban ini mengenai kasus spesifik yang Anda tanyakan. Ada pendekatan umum yang teman dan saya telah dikembangkan yang memungkinkan untuk sewenang-wenang mengutip dalam kasus Anda perlu untuk kutipan pesta perintah melalui beberapa lapisan ekspansi shell, misalnya, melalui ssh, su -c
, bash -c
, dll Ada satu inti primitif yang Anda butuhkan, di sini dalam bash asli:
quote_args() {
local sq="'"
local dq='"'
local space=""
local arg
for arg; do
echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
space=" "
done
}
Ini tidak persis seperti yang dikatakannya: ia mengutip setiap argumen secara individual (tentu saja setelah ekspansi bash):
$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'
Ini melakukan hal yang jelas untuk satu lapisan ekspansi:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Perhatikan bahwa tanda kutip ganda $(quote_args ...)
diperlukan untuk menjadikan hasilnya menjadi argumen tunggal bash -c
.) Dan ini dapat digunakan secara lebih umum untuk mengutip dengan benar melalui beberapa lapis ekspansi:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Contoh di atas:
- shell-quote setiap argumen ke inner secara
quote_args
individual dan kemudian menggabungkan output yang dihasilkan menjadi argumen tunggal dengan inner double quotes.
- shell-quotes
bash
,, -c
dan hasil yang sudah pernah dikutip dari langkah 1, dan kemudian menggabungkan hasilnya menjadi argumen tunggal dengan tanda kutip ganda luar.
- mengirimkan kekacauan itu sebagai argumen ke luar
bash -c
.
Singkatnya, gagasan itu. Anda dapat melakukan beberapa hal yang cukup rumit dengan ini, tetapi Anda harus berhati-hati tentang urutan evaluasi dan tentang substring mana yang dikutip. Misalnya, berikut ini melakukan hal-hal yang salah (untuk beberapa definisi "salah"):
$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure
Pada contoh pertama, bash segera mengembang quote_args cd /; pwd 1>&2
menjadi dua perintah terpisah, quote_args cd /
dan pwd 1>&2
, jadi CWD masih /tmp
ketika pwd
perintah dieksekusi. Contoh kedua menggambarkan masalah yang sama untuk globbing. Memang, masalah dasar yang sama terjadi dengan semua ekspansi bash. Masalahnya di sini adalah substitusi perintah bukanlah pemanggilan fungsi: ini secara harfiah mengevaluasi satu skrip bash dan menggunakan outputnya sebagai bagian dari skrip bash lain.
Jika Anda mencoba melarikan diri dari operator shell, Anda akan gagal karena string yang dihasilkan diteruskan bash -c
hanya rangkaian string yang dikutip secara individual yang tidak kemudian diartikan sebagai operator, yang mudah dilihat jika Anda menggemakan string yang akan telah diteruskan ke bash:
$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
Masalahnya di sini adalah Anda terlalu banyak mengutip. Yang Anda butuhkan adalah agar operator tidak mengutip sebagai input pada lampiranbash -c
, yang berarti mereka harus berada di luar $(quote_args ...)
substitusi perintah.
Akibatnya, apa yang perlu Anda lakukan dalam pengertian yang paling umum adalah dengan mengutip setiap kata dari perintah yang tidak dimaksudkan untuk diperluas pada saat penggantian perintah secara terpisah, dan tidak menerapkan penawaran tambahan untuk operator shell:
$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success
Setelah Anda melakukan ini, seluruh string adalah permainan yang adil untuk mengutip lebih lanjut ke tingkat evaluasi sewenang-wenang:
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success
dll.
Contoh-contoh ini mungkin kelihatan berlebihan mengingat bahwa kata-kata seperti success
,, sbin
dan pwd
tidak perlu dikutip dengan shell, tetapi poin kunci yang perlu diingat ketika menulis sebuah skrip mengambil input sewenang-wenang adalah bahwa Anda ingin mengutip semua yang Anda tidak benar-benar yakin tidak. ' tidak perlu mengutip, karena Anda tidak pernah tahu kapan pengguna akan melempar Robert'; rm -rf /
.
Untuk lebih memahami apa yang terjadi di balik selimut, Anda dapat bermain-main dengan dua fungsi pembantu kecil:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
yang akan menyebutkan setiap argumen ke perintah sebelum menjalankannya:
$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2