Większość z tych odpowiedzi dotyczy konkretnego przypadku, o który pytasz. Istnieje ogólne podejście, że przyjaciel i ja stworzyliśmy, która pozwala na arbitralne cytowanie w przypadku trzeba cytat bash polecenia za pośrednictwem wielu warstw powłoki, np ekspansji, poprzez ssh su -c, bash -citp Jest jeden rdzeń prymitywny trzeba tutaj w natywnej 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
}
Robi dokładnie to, co mówi: powtarza każdy argument osobno (oczywiście po rozszerzeniu basha):
$ 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'
Robi to oczywiste dla jednej warstwy rozszerzenia:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Zauważ, że podwójne cudzysłowy $(quote_args ...)są konieczne, aby wynik był pojedynczym argumentem bash -c.) I można go bardziej ogólnie użyć do cytowania poprawnie poprzez wiele warstw rozwinięcia:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Powyższy przykład:
- shell cytuje każdy argument wewnętrznie
quote_argsindywidualnie, a następnie łączy wynikowy wynik w pojedynczy argument z wewnętrznymi podwójnymi cudzysłowami.
- Shell notowania
bash, -ci już po cytowane wynikiem z etapu 1, a następnie łączy się wynik w jeden argument z zewnętrznych cudzysłowach.
- wysyła ten bałagan jako argument na zewnątrz
bash -c.
Taki jest pomysł w pigułce. Możesz robić z tym dość skomplikowane rzeczy, ale musisz uważać na kolejność oceny i na cytowane podciągi. Na przykład następujące czynności są niewłaściwe (w przypadku niektórych definicji „zła”):
$ (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
W pierwszym przykładzie bash natychmiast rozwija się quote_args cd /; pwd 1>&2w dwie osobne komendy, quote_args cd /a pwd 1>&2więc CWD jest nadal w /tmptrakcie wykonywania pwdkomendy. Drugi przykład ilustruje podobny problem z globowaniem. Rzeczywiście ten sam podstawowy problem występuje w przypadku wszystkich rozszerzeń bash. Problem polega na tym, że podstawienie polecenia nie jest wywołaniem funkcji: dosłownie ocenia jeden skrypt bash i wykorzystuje jego dane wyjściowe jako część innego skryptu bash.
Jeśli spróbujesz po prostu uciec operatorom powłoki, nie powiedzie się, ponieważ przekazany ciąg znaków bash -cjest po prostu sekwencją pojedynczo cytowanych ciągów, które nie są następnie interpretowane jako operatory, co jest łatwe do zauważenia, jeśli powtórzy się ciąg zostały przekazane do 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'
Problem polega na tym, że przesadzasz. Potrzebne jest, aby operatory nie były cytowane jako dane wejściowe do załączania bash -c, co oznacza, że muszą znajdować się poza $(quote_args ...)podstawianiem poleceń.
W związku z tym, co musisz zrobić w najbardziej ogólnym sensie, to zacytowanie powłoki każdego słowa polecenia, które nie jest przeznaczone do rozszerzenia w momencie podstawiania polecenia osobno, i nie stosuj żadnego dodatkowego cudzysłowu dla operatorów powłoki:
$ (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
Gdy to zrobisz, cały ciąg jest uczciwą grą do dalszego cytowania na dowolnych poziomach oceny:
$ 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
itp.
Te przykłady mogą wydawać wymęczony zważywszy, że słowa takie jak success, sbini pwdnie trzeba być powłoka cytowany, ale kluczowy punkt, aby pamiętać przy pisaniu skryptu biorąc dowolny wejście jest, że chcesz zacytować wszystko nie jesteś absolutnie pewny robi” t trzeba przytoczyć, bo nigdy nie wiadomo, kiedy użytkownik będzie rzucać w Robert'; rm -rf /.
Aby lepiej zrozumieć, co dzieje się pod przykryciem, możesz bawić się przy pomocy dwóch małych funkcji pomocniczych:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
, który wyliczy każdy argument do polecenia przed jego wykonaniem:
$ 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