Bu yanıtların çoğu, sorduğunuz özel davaya çarpıyor. Orada bir arkadaş olduğunu genel bir yaklaşımdır ve ben keyfi size ihtiyacı olduğunda alıntı için izin verdiğini geliştirdik için alıntı bash komutları ssh'dan kabuk genişleme, örneğin çoklu katmanları boyunca, su -c
, bash -c
, tek çekirdekli ilkel burada, lüzum yoktur vs. yerli bash:
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
}
Bu tam olarak söylediği şeyi yapar: her argümanı ayrı ayrı kabuklandırır (elbette bash genişletmesinden sonra):
$ 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'
Bir genişleme katmanı için bariz bir şey yapar:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
( $(quote_args ...)
Sonucun tek bir argüman haline getirilmesi için etraftaki çift tırnak işaretlerinin gerekli olduğunu unutmayın bash -c
.) Ve daha genel olarak birden çok genişletme katmanı aracılığıyla düzgün bir şekilde alıntı yapmak için kullanılabilir:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Yukarıdaki örnek:
- shell-her argümanı içsel olarak
quote_args
ayrı ayrı tırnak içine alır ve daha sonra elde edilen çıktıyı iç çift tırnak ile tek bir argümanda birleştirir.
- kabuk tırnak
bash
, -c
ve 1. adımdan itibaren zaten bir kere alıntılanan sonuç ve ardından dış çift tırnak ile tek bir argüman sonucu birleştirir.
- bu karışıklığı dışa argüman olarak gönderir
bash -c
.
Kısaca fikir budur. Bununla oldukça karmaşık şeyler yapabilirsiniz, ancak değerlendirme sırası ve hangi alt dizelerin alıntılandığı konusunda dikkatli olmalısınız. Örneğin, aşağıdakiler yanlış şeyler yapar (bazı "yanlış" tanımları için):
$ (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
İlk örnekte, bash hemen quote_args cd /; pwd 1>&2
iki ayrı komuta genişler quote_args cd /
ve pwd 1>&2
bu nedenle komut yürütüldüğünde CWD hala /tmp
olur pwd
. İkinci örnek, globbing için benzer bir sorunu göstermektedir. Aslında, aynı temel sorun tüm bash genişlemelerinde ortaya çıkar. Buradaki sorun, komut değiştirme işleminin bir işlev çağrısı olmamasıdır: kelimenin tam anlamıyla bir bash betiğini değerlendirir ve çıktısını başka bir bash betiğinin parçası olarak kullanır.
Kabuk işleçlerinden basitçe kaçmaya çalışırsanız, sonuçta elde edilen dize bash -c
, yalnızca işleç olarak yorumlanmayan tek tek tırnaklı dizelerden oluşan bir dizidir, bu da dizeyi yankılandıracağınızı görmek kolaydır bash'a geçti:
$ (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'
Buradaki sorun fazla alıntı yapmanız. İhtiyacınız olan şey, operatörlerin çevreye giriş olarak ayrıştırılmamasıdır bash -c
, yani $(quote_args ...)
komut ikamesinin dışında olmaları gerekir .
Sonuç olarak, en genel anlamda yapmanız gereken, komut ikamesi sırasında ayrı olarak genişletilmesi amaçlanmayan komutun her bir kelimesini ayrı ayrı kabuk-alıntı yapmak ve kabuk operatörlerine herhangi bir ekstra alıntı uygulamak değildir:
$ (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
Bunu yaptıktan sonra, dizenin tamamı keyfi değerlendirme seviyelerine daha fazla alıntı yapmak için adil bir oyundur:
$ 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
vb.
Bu örnekler sinirleri sözler gibi göz önüne alındığında görünebilir success
, sbin
ve pwd
kabuk-alıntılanan olması gerekmez, ama keyfi girdi alarak bir senaryo yazarken önemli nokta hatırlamak size kesinlikle emin değiliz herşeyi alıntı yapmak istiyorum olmasıdır 'doesn t Alıntı yapmak gerekmez, çünkü bir kullanıcının ne zaman atacağını asla bilemezsiniz Robert'; rm -rf /
.
Kapakların altında neler olup bittiğini daha iyi anlamak için iki küçük yardımcı işlevle oynayabilirsiniz:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
bir komutu yürütmeden önce her bir argümanı numaralandıracak:
$ 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