Die meisten dieser Antworten treffen auf den speziellen Fall, nach dem Sie fragen. Es gibt einen allgemeinen Ansatz , dass ein Freund und ich habe entwickelt, das für beliebige zitieren , falls erlaubt müssen Sie zitieren bash Befehle durch mehrere Schichten von Shell - Erweiterung, zum Beispiel durch ssh, su -c
, bash -c
etc. Es gibt einen Kern primitiv ist , dass Sie benötigen, hier in native 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
}
Dies macht genau das, was es sagt: Es zitiert jedes Argument einzeln (natürlich nach der Bash-Erweiterung):
$ 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'
Es ist das Offensichtliche für eine Expansionsschicht:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Beachten Sie, dass die doppelten Anführungszeichen $(quote_args ...)
erforderlich sind, um das Ergebnis in ein einziges Argument umzuwandeln bash -c
.) Und es kann allgemeiner verwendet werden, um durch mehrere Erweiterungsebenen richtig zu zitieren:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Das obige Beispiel:
- Shell zitiert jedes Argument
quote_args
einzeln in das Innere und kombiniert dann die resultierende Ausgabe zu einem einzigen Argument mit den inneren doppelten Anführungszeichen.
- Shell-Zitate
bash
, -c
und das schon einmal zitierte Ergebnis aus Schritt 1, und kombinieren dann das Ergebnis in ein einziges Argument mit den äußeren Anführungszeichen.
- sendet dieses Chaos als Argument an die äußere
bash -c
.
Das ist die Idee auf den Punkt gebracht. Sie können einige ziemlich komplizierte Dinge damit machen, aber Sie müssen vorsichtig sein, in welcher Reihenfolge die Bewertung erfolgt und welche Teilzeichenfolgen zitiert werden. Zum Beispiel machen die folgenden Dinge die falschen Dinge (für eine Definition von "falsch"):
$ (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
Im ersten Beispiel, dehnt bash sofort quote_args cd /; pwd 1>&2
in zwei getrennte Befehle, quote_args cd /
und pwd 1>&2
, so wie vor der CWD ist , /tmp
wenn der pwd
Befehl ausgeführt wird. Das zweite Beispiel zeigt ein ähnliches Problem beim Globbing. In der Tat tritt das gleiche Grundproblem bei allen Bash-Erweiterungen auf. Das Problem hierbei ist, dass eine Befehlssubstitution kein Funktionsaufruf ist: Sie wertet buchstäblich ein Bash-Skript aus und verwendet seine Ausgabe als Teil eines anderen Bash-Skripts.
Wenn Sie versuchen, den Shell-Operatoren einfach zu entkommen, schlagen Sie fehl, da die resultierende Zeichenfolge, an die übergeben bash -c
wird, nur eine Folge von Zeichenfolgen in Anführungszeichen ist, die dann nicht als Operatoren interpretiert werden. Dies ist leicht zu erkennen, wenn Sie die Zeichenfolge wiedergeben, die dies tun würde wurden an bash übergeben:
$ (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'
Das Problem hier ist, dass Sie zu viel zitieren. Was Sie brauchen, ist, dass die Operatoren nicht als Eingabe für das Gehäuse in Anführungszeichen gesetzt werden bash -c
, was bedeutet, dass sie sich außerhalb der $(quote_args ...)
Befehlsersetzung befinden müssen.
Folglich müssen Sie im allgemeinsten Sinne jedes Wort des Befehls in Shell zitieren, das zum Zeitpunkt der Befehlssubstitution nicht erweitert werden soll, und keine zusätzlichen Anführungszeichen auf die Shell-Operatoren anwenden:
$ (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
Sobald Sie dies getan haben, ist die gesamte Zeichenfolge ein faires Spiel, um weitere Bewertungsstufen zu zitieren:
$ 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
usw.
Diese Beispiele können overwrought scheinen gegeben , dass Worte wie success
, sbin
und pwd
nicht seine muschel zitiert müssen, aber der entscheidende Punkt zu erinnern , wenn ein Skript unter beliebiger Eingabe zu schreiben ist , dass man alles zitieren wollen Sie nicht absolut sicher doesn‘ Ich brauche kein Zitat, weil Sie nie wissen, wann ein Benutzer eine einwirft Robert'; rm -rf /
.
Um besser zu verstehen, was unter der Decke vor sich geht, können Sie mit zwei kleinen Hilfsfunktionen herumspielen:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
Dadurch werden alle Argumente zu einem Befehl aufgelistet, bevor sie ausgeführt werden:
$ 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