Die meisten dieser Antworten treffen auf den konkreten Fall, nach dem Sie fragen. Es gibt einen allgemeinen Ansatz, den ein Freund und ich entwickelt haben, der willkürliches Zitieren für den Fall erlaubt, dass Sie Bash-Befehle durch mehrere Ebenen der Shell-Expansion zitieren müssen, z. B. durch ssh, su -c
, bash -c
, usw. Es gibt ein grundlegendes Primitiv, das Sie brauchen, hier in der nativen 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 tut genau das, was es sagt: Es zitiert jedes Argument einzeln (natürlich nach der Bash-Expansion):
$ 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 handelt sich um eine offensichtliche Erweiterung um eine Schicht:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Beachten Sie, dass die doppelten Anführungszeichen um $(quote_args ...)
sind notwendig, um das Ergebnis zu einem einzigen Argument für bash -c
.) Und es kann allgemeiner verwendet werden, um durch mehrere Erweiterungsschichten richtig zu zitieren:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Das obige Beispiel:
- shell-quotes jedes Argument an die innere
quote_args
einzeln und fasst dann die resultierende Ausgabe in einem einzigen Argument mit den inneren Anführungszeichen zusammen.
- Shell-Anführungszeichen
bash
, -c
und das bereits in Anführungszeichen gesetzte Ergebnis aus Schritt 1 und kombiniert dann das Ergebnis zu einem einzigen Argument mit den äußeren Anführungszeichen.
- sendet dieses Chaos als Argument an die äußere
bash -c
.
Das ist der Grundgedanke, kurz und bündig. Man kann damit ziemlich komplizierte Sachen machen, aber man muss auf die Reihenfolge der Auswertung achten und darauf, welche Teilzeichenfolgen zitiert werden. Zum Beispiel machen die folgenden Dinge das Falsche (für irgendeine 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 expandiert die Bash sofort quote_args cd /; pwd 1>&2
in zwei getrennte Befehle, quote_args cd /
y pwd 1>&2
Die CWD ist also immer noch /tmp
wenn die pwd
Befehl ausgeführt wird. Das zweite Beispiel veranschaulicht ein ähnliches Problem für Globbing. In der Tat tritt das gleiche Grundproblem bei allen Bash-Erweiterungen auf. Das Problem ist, dass eine Befehlssubstitution kein Funktionsaufruf ist: Sie wertet buchstäblich ein Bash-Skript aus und verwendet dessen Ausgabe als Teil eines anderen Bash-Skripts.
Wenn Sie versuchen, die Shell-Operatoren einfach zu escapen, werden Sie scheitern, weil die resultierende Zeichenkette, die an bash -c
ist nur eine Folge von einzeln in Anführungszeichen gesetzten Zeichenketten, die dann nicht als Operatoren interpretiert werden, was leicht zu erkennen ist, wenn man die Zeichenkette, die an die Bash übergeben wurde, echot:
$ (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 in Anführungszeichen gesetzt werden, sondern als Eingabe für die umschließende bash -c
was bedeutet, dass sie sich außerhalb der $(quote_args ...)
Befehlssubstitution.
Folglich müssen Sie im allgemeinsten Sinne jedes Wort des Befehls, das zum Zeitpunkt der Befehlssubstitution nicht erweitert werden soll, separat in Anführungszeichen setzen 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 Zeichenkette Freiwild für weitere Zitate auf beliebigen Auswertungsebenen:
$ 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
etc.
Diese Beispiele mögen überspitzt erscheinen, da Worte wie success
, sbin
y pwd
müssen nicht in Shell-Zeichen gesetzt werden, aber der wichtigste Punkt, den man sich merken sollte, wenn man ein Skript schreibt, das willkürliche Eingaben entgegennimmt, ist, dass man alles zitieren sollte, bei dem man sich nicht absolut sicher ist nicht Anführungszeichen brauchen, weil man nie weiß, wann ein Nutzer eine Frage einwirft Robert'; rm -rf /
.
Um besser zu verstehen, was hinter den Kulissen vor sich geht, können Sie mit zwei kleinen Hilfsfunktionen spielen:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
die jedes Argument eines Befehls aufzählt, bevor er ausgeführt wird:
$ 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
33 Stimmen
Ist Ihnen klar, dass Sie für Alias keine einfachen Anführungszeichen verwenden müssen? Doppelte Anführungszeichen sind viel einfacher.
8 Stimmen
Siehe auch: Unterschied zwischen einfachen und doppelten Anführungszeichen in der Bash .
6 Stimmen
Verschachtelte doppelte Anführungszeichen sind ausweichbar,
"\""
daher sollten diese, wann immer möglich, der Antwort von @liori vorgezogen werden.30 Stimmen
Doppelte Anführungszeichen verhalten sich in *nix (einschließlich Bash und verwandten Werkzeugen wie Perl) ganz anders als einfache Anführungszeichen, so dass es KEINE gute Lösung ist, doppelte Anführungszeichen zu ersetzen, wenn es ein Problem mit einfachen Anführungszeichen gibt. Doppelte Anführungszeichen geben an, dass $... Variablen vor der Ausführung ersetzt werden sollen, während einfache Anführungszeichen angeben, dass $... wörtlich zu behandeln sind.
1 Stimmen
Wenn Sie denken, Ich habe doppelte Anführungszeichen verwendet, aber es funktioniert immer noch nicht. , geben Sie Ihr Skript erneut ein.
0 Stimmen
Der letzte Versuch war knapp, entfernen Sie einfach das letzte Zitat.