Ich benutze GNU bash
4.3.48
. Betrachten Sie die folgenden zwei Befehle, die sich nur durch ein einzelnes Dollarzeichen unterscheiden.
Befehl 1:
echo "(echo " * ")"
Befehl 2:
echo "$(echo " * ")"
Die Ausgabe von ihnen ist jeweils
(echo test.txt ppcg.sh )
und
*
Im ersten Fall *
ist das also offensichtlich globbed, was bedeutet, dass das erste Anführungszeichen mit dem zweiten ein Paar bildet und das dritte und das vierte ein weiteres Paar bilden.
Im zweiten Fall *
ist das nicht globbed, und es gibt genau zwei zusätzliche Leerzeichen in der Ausgabe, eins vor dem Sternchen und eins nach dem Sternchen, was bedeutet, dass das zweite Anführungszeichen mit dem dritten und das erste mit dem vierten übereinstimmt.
Gibt es außer der $()
Konstruktion noch andere Fälle , in denen die Anführungszeichen nicht mit dem nächsten übereinstimmen, sondern stattdessen verschachtelt sind? Ist dieses Verhalten gut dokumentiert und wenn ja, wo finde ich das entsprechende Dokument?
Antworten:
Jedes der Verschachtelungskonstrukte, die in Zeichenfolgen interpoliert werden können, kann weitere Zeichenfolgen enthalten: Sie werden wie ein neues Skript bis zur abschließenden Markierung analysiert und können sogar mehrere Ebenen tief verschachtelt werden. Alle Balken beginnen mit einem
$
. Alle von ihnen sind in einer Kombination aus Bash-Handbuch und POSIX-Shell-Befehlssprachenspezifikation dokumentiert.Es gibt einige Fälle dieser Konstrukte:
Befehlsersetzung mit
$( ... )
, wie Sie gefunden haben. POSIX gibt dieses Verhalten an :Anführungszeichen sind Teil gültiger Shell-Skripte, daher sind sie mit ihrer normalen Bedeutung zulässig.
`
.Das "Wort" -Element von Instanzen
${parameter:-word}
mit erweiterter Parametersubstitution wie z . Die Definition von "Wort" ist :- der zitierten Text und sogar gemischte Zitate enthält
a"b"c'd'e
- obwohl das tatsächliche Verhalten der Erweiterungen etwas liberaler ist und zum Beispiel auch${x:-hello world}
funktioniert.Arithmetische Erweiterung mit
$(( ... ))
, obwohl es dort weitgehend nutzlos ist (aber Sie können auch Befehlssubstitutionen oder variable Erweiterungen verschachteln und dann sinnvollerweise Anführungszeichen in diesen verwenden). POSIX gibt an, dass :Daher ist dieses Verhalten ausdrücklich erforderlich. Das heißt
echo "abc $((4 "*" 5))"
, arithmetisch, anstatt zu klackern.Beachten Sie jedoch, dass
$[ ... ]
arithmetische Erweiterungen alten Stils nicht auf die gleiche Weise behandelt werden: Anführungszeichen sind ein Fehler, wenn sie angezeigt werden, unabhängig davon, ob die Erweiterung in Anführungszeichen steht oder nicht. Dieses Formular ist nicht mehr dokumentiert und soll auch nicht mehr verwendet werden.$"..."
, die eigentlich das"
als Kernelement verwendet.$"
wird als eine Einheit behandelt.Es gibt noch einen weiteren Verschachtelungsfall, den Sie möglicherweise nicht erwarten und der keine Anführungszeichen enthält, nämlich die Erweiterung in
{a,b{c,d},e}
geschweifte Klammern : Wird zu "a bc bd e" erweitert.${x:-a{b,c}d}
tut nicht nisten, jedoch; Es wird als Parametersubstitution behandelt, die "a{b,c
" gefolgt von "d}
" ergibt . Das ist auch dokumentiert :In der Regel analysieren alle abgegrenzten Konstrukte ihre Körper unabhängig vom umgebenden Kontext (und Ausnahmen werden als Bugs behandelt ).
$(
Wenn der Befehlssubstitutionscode angezeigt wird, fordert er den Parser im Wesentlichen auf, so viel wie möglich aus dem Body zu konsumieren, als wäre es ein neues Programm. Anschließend wird überprüft, ob der erwartete Abschlussmarker (ein nicht entkapptes)
oder))
oder}
) angezeigt wird, sobald der Subparser ausgeführt wird von Dingen, die es verbrauchen kann.Wenn Sie über die Funktionsweise eines Parsers für rekursive Abstiegsdaten nachdenken , ist dies nur eine einfache Rekursion zum Basisfall. Es ist tatsächlich einfacher als andersrum, wenn Sie überhaupt eine String-Interpolation haben. Unabhängig von der zugrunde liegenden Analysetechnik führen Shells, die diese Konstrukte unterstützen, zu demselben Ergebnis.
Durch diese Konstrukte können Sie das Zitieren so tief verschachteln, wie Sie möchten, und es funktioniert wie erwartet. Nirgendwo wird es verwirrend sein, ein Zitat in der Mitte zu sehen. Stattdessen ist dies der Beginn einer neuen Zeichenfolge in Anführungszeichen im inneren Kontext.
quelle
"blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")"
, warum ist die zweiten doppelten Anführungszeichen nicht das Ende der ersten doppelten Anführungszeichen (wie durch die Syntax gezeigt in Ihrer Antwort hervorgehoben), aber ein Anfang einer Zeichenfolge innerhalb$(...)
? Liegt es daran, dass der Bash-Parser von oben nach unten und nicht von unten nach oben ist?"${var-"foo"}"
(echo "${-+"*"}"
wie zum Beispielecho *
in der Bourne- oder Korn-Shell), und das Verhalten wird in der nächsten Version des Standards deutlich unspezifiziert . Siehe auch Diskussion unter mail-archive.com/[email protected]/msg00167.htmlVielleicht hilft ein Blick auf die beiden Beispiele mit
printf
(anstelle vonecho
):Es werden
(echo
(das erste Wort, einschließlich eines nachgestellten Leerzeichens), einige Dateien und das Schlusswort gedruckt)
.Die Klammer ist nur ein Teil der in Anführungszeichen gesetzten Zeichenfolge
(echo
.Das Sternchen (jetzt ohne Anführungszeichen, da die beiden Anführungszeichen gepaart sind) wird als Glob zu einer Liste übereinstimmender Dateien erweitert.
Und dann die schließende Klammer.
Ihr zweiter Befehl funktioniert jedoch wie folgt:
Das
$
startet eine Befehlsersetzung. Das fängt ganz frisch an zu zitieren.Das Sternchen ist in Anführungszeichen gesetzt,
" * "
und das ist, was der Befehl (hier ist es ein Befehl und keine in Anführungszeichen gesetzte Zeichenfolge)echo
ausgibt. Zum Schlussprintf
formatieren Sie das neu*
und drucken es als< * >
.quelle