La maggior parte di queste risposte colpisce il caso specifico di cui stai chiedendo. V'è un approccio generale che un amico e ho sviluppato che consente arbitrario citare in caso di necessità di preventivo bash comandi attraverso strati multipli di espansione shell, ad esempio, tramite SSH, su -c
, bash -c
, ecc C'è un nucleo primitivo è necessario, qui in bash nativo:
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
}
Questo fa esattamente quello che dice: shell cita ogni argomento singolarmente (dopo l'espansione bash, ovviamente):
$ 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'
Fa la cosa ovvia per uno strato di espansione:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Nota che le virgolette doppie $(quote_args ...)
sono necessarie per trasformare il risultato in un singolo argomento in bash -c
.) E può essere usato più in generale per citare correttamente attraverso più livelli di espansione:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
L'esempio sopra:
- shell cita ogni argomento all'interno
quote_args
singolarmente e quindi combina l'output risultante in un singolo argomento con le doppie virgolette interne.
- shell-quotes
bash
, -c
e il risultato già citato una volta dal passaggio 1, quindi combina il risultato in un singolo argomento con le doppie virgolette esterne.
- invia quel casino come argomento verso l'esterno
bash -c
.
Questa è l'idea in breve. Puoi fare alcune cose piuttosto complicate con questo, ma devi stare attento all'ordine di valutazione e a quali sottostringhe vengono citate. Ad esempio, quanto segue fa le cose sbagliate (per alcune definizioni di "sbagliato"):
$ (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
Nel primo esempio, bash si espande immediatamente quote_args cd /; pwd 1>&2
in due comandi separati quote_args cd /
e pwd 1>&2
, quindi, il CWD rimane fermo /tmp
quando il pwd
comando viene eseguito. Il secondo esempio illustra un problema simile per il globbing. In effetti, lo stesso problema di base si verifica con tutte le espansioni di bash. Il problema qui è che una sostituzione di comando non è una chiamata di funzione: sta letteralmente valutando uno script bash e usando il suo output come parte di un altro script bash.
Se provi semplicemente a sfuggire agli operatori di shell, fallirai perché la stringa risultante passata bash -c
è solo una sequenza di stringhe quotate singolarmente che non vengono quindi interpretate come operatori, che è facile vedere se fai eco alla stringa che sono stati passati a 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'
Il problema qui è che stai sopravvalutando. Ciò di cui hai bisogno è che gli operatori non vengano citati come input per l'allegato bash -c
, il che significa che devono essere al di fuori della $(quote_args ...)
sostituzione del comando.
Di conseguenza, ciò che è necessario fare nel senso più generale è citare una shell per citare ogni parola del comando che non si intende espandere al momento della sostituzione del comando separatamente e non applicare alcuna citazione aggiuntiva agli operatori della 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
Una volta fatto questo, l'intera stringa è un gioco equo per ulteriori citazioni a livelli arbitrari di valutazione:
$ 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
eccetera.
Questi esempi possono sembrare esagerata dato che parole come success
, sbin
e pwd
non hanno bisogno di essere shell citato, ma il punto chiave da ricordare quando si scrive una sceneggiatura prendendo l'input arbitraria è che si vuole citare tutto quello che non si è assolutamente sicuri doesn' Ho bisogno di un preventivo, perché non sai mai quando un utente inserirà un Robert'; rm -rf /
.
Per capire meglio cosa sta succedendo sotto le coperte, puoi giocare con due piccole funzioni di supporto:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
che enumererà ogni argomento in un comando prima di eseguirlo:
$ 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