1433 Stimmen

Wie werden einfache Anführungszeichen innerhalb von Zeichenketten mit einfachen Anführungszeichen aufgehoben?

Nehmen wir an, Sie haben einen Bash alias mögen:

alias rxvt='urxvt'

was gut funktioniert.

Allerdings:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

wird nicht funktionieren, und das wird es auch nicht:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

Wie lassen sich also öffnende und schließende Anführungszeichen innerhalb einer Zeichenkette zuordnen, wenn Sie Anführungszeichen escaped haben?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

scheint unhandlich, obwohl es dieselbe Zeichenkette darstellen würde, wenn man sie auf diese Weise verketten darf.

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

6 Stimmen

Verschachtelte doppelte Anführungszeichen sind ausweichbar, "\"" daher sollten diese, wann immer möglich, der Antwort von @liori vorgezogen werden.

14voto

Rob Jens Punkte 524

Ich verwende nur Shell-Codes, z. B. \x27 o \\x22 wie zutreffend. Kein Ärger, niemals wirklich.

3 Stimmen

Könnten Sie ein Beispiel für diese Vorgehensweise zeigen? Bei mir wird einfach ein Literal gedruckt x27 (auf Centos 6.6)

2 Stimmen

@WillSheppard echo -e "\x27 \\x22" druckt ' "

1 Stimmen

@WillSheppard und andere, hier sind eine Reihe von Beispielen, die ich gerade geschrieben habe: stackoverflow.com/a/65878993/4561887 .

12voto

Abbas Gadhia Punkte 13742

Da man keine einfachen Anführungszeichen innerhalb von Zeichenketten mit einfachen Anführungszeichen setzen kann, ist die einfachste und lesbarste Option die Verwendung einer HEREDOC-Zeichenkette

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

In dem obigen Code wird der HEREDOC an die cat und die Ausgabe davon wird über die Befehlssubstitutionsschreibweise einer Variablen zugewiesen $(..)

Ein einfaches Anführungszeichen um den HEREDOC ist erforderlich, da er sich innerhalb einer $()

0 Stimmen

Ich wünschte, ich hätte vorher so weit nach unten gescrollt - ich habe diesen Ansatz neu erfunden und bin hierher gekommen, um ihn zu posten! Das ist viel sauberer und lesbarer als all die anderen Escape-Ansätze. Es wird nicht auf einigen Nicht-Bash-Shells funktionieren, wie z.B. dash die die Standard-Shell in den Ubuntu-Startskripten und anderswo ist.

0 Stimmen

Vielen Dank! Das habe ich gesucht, die Möglichkeit, einen Befehl zu definieren, wie es über heredoc ist und den auto escaped Befehl an ssh zu übergeben. BTW cat <<COMMAND ohne Anführungszeichen erlaubt die Interpolation von Variablen innerhalb des Befehls und funktioniert auch für diesen Ansatz.

11voto

teknopaul Punkte 6101

IMHO ist die eigentliche Antwort, dass man einfache Anführungszeichen in einfachen Anführungszeichen nicht entschlüsseln kann.

Das ist unmöglich.

Wenn wir davon ausgehen, dass wir die Bash verwenden.

Aus dem bash-Handbuch...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

Sie müssen einen der anderen String-Escape-Mechanismen " oder \ verwenden.

Es gibt nichts Magisches an alias die die Verwendung einfacher Anführungszeichen verlangt.

Die beiden folgenden funktionieren in der Bash.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

Bei letzterem wird \ verwendet, um das Leerzeichen zu umgehen.

Es gibt auch nichts Magisches an #111111, das einfache Anführungszeichen erfordert.

Die folgenden Optionen führen zu demselben Ergebnis wie die beiden anderen Optionen, da der rxvt-Alias wie erwartet funktioniert.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

Sie können das lästige # auch direkt weglassen

alias rxvt="urxvt -fg \#111111 -bg \#111111"

0 Stimmen

"Die wirkliche Antwort ist, dass man einfache Anführungszeichen in einfachen Anführungszeichen nicht umgehen kann." Das ist technisch gesehen richtig. Aber Sie können eine Lösung haben, die mit einem einfachen Anführungszeichen beginnt, mit einem einfachen Anführungszeichen endet und nur in der Mitte einfache Anführungszeichen enthält. stackoverflow.com/a/49063038

2 Stimmen

Nicht durch Escaping, sondern nur durch Verkettung.

7voto

Kyle Rose Punkte 91

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:

  1. 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.
  2. 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.
  3. 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

0 Stimmen

Hallo Kyle. Ihre Lösung funktionierte gut für einen Fall, den ich hatte, wenn ich eine Gruppe von Argumenten als ein einzelnes Argument übergeben musste: vagrant ssh -c {single-arg} guest . Die {single-arg} muss als einzelnes Argument behandelt werden, da Vagrant das nächste Argument als Gastnamen verwendet. Die Reihenfolge kann nicht geändert werden. Ich musste jedoch einen Befehl und seine Argumente innerhalb von {single-arg} . Also habe ich Ihre quote_args() um den Befehl und seine Argumente in Anführungszeichen zu setzen und das Ergebnis in doppelte Anführungszeichen zu setzen, und es hat wunderbar funktioniert: vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest . Danke!!!

5voto

wisbucky Punkte 26902

Natürlich wäre es einfacher, den Text einfach in Anführungszeichen zu setzen, aber wo ist da die Herausforderung? Hier ist die Antwort, die nur einfache Anführungszeichen verwendet. Ich verwende eine Variable anstelle von alias so dass es einfacher ist, einen Probedruck zu erstellen, aber es ist dasselbe wie bei der Verwendung von alias .

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

Erläuterung

Der Schlüssel ist, dass Sie das einfache Anführungszeichen schließen und beliebig oft wieder öffnen können. Zum Beispiel foo='a''b' ist dasselbe wie foo='ab' . Sie können also das einfache Anführungszeichen schließen und ein wörtliches einfaches Anführungszeichen einfügen \' und öffnen Sie dann das nächste einfache Anführungszeichen erneut.

Pannenplan

In diesem Diagramm wird durch die Verwendung von Klammern deutlich, wo die einfachen Anführungszeichen geöffnet und geschlossen werden. Anführungszeichen sind nicht "verschachtelt", wie es bei Klammern der Fall sein kann. Sie können auch auf die farbliche Hervorhebung achten, die korrekt angewendet wird. Die in Anführungszeichen gesetzten Zeichenfolgen sind kastanienbraun, während die \' ist schwarz.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(Dies ist im Wesentlichen die gleiche Antwort wie die von Adrian, aber ich denke, dass sie besser erklärt wird. Außerdem hat seine Antwort 2 überflüssige einfache Anführungszeichen am Ende).

0 Stimmen

+1 für die Verwendung des '\'' Methode empfehle ich über die '"'"' Methode, die für den Menschen oft schwieriger zu lesen ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X