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 -c
itp 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_args
indywidualnie, a następnie łączy wynikowy wynik w pojedynczy argument z wewnętrznymi podwójnymi cudzysłowami.
- Shell notowania
bash
, -c
i 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>&2
w dwie osobne komendy, quote_args cd /
a pwd 1>&2
więc CWD jest nadal w /tmp
trakcie wykonywania pwd
komendy. 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 -c
jest 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
, sbin
i pwd
nie 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