In dieser Frage meldet jemand ein Problem bei der Verwendung eines Here-Dokuments mit einem in Anführungszeichen gesetzten Begrenzungswort innerhalb der $(...)
Befehlsersetzung , wobei ein Backslash \
am Ende einer Zeile innerhalb des Dokuments die Fortsetzung von Zeilen mit Zeilenumbrüchen auslöst , während dasselbe Here-Dokument außerhalb der Befehlsersetzung wie erwartet funktioniert .
Hier ist ein vereinfachtes Beispieldokument:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
Dies beinhaltet einen Backtick und einen Backslash am Ende einer Zeile. Das Begrenzungszeichen wird in Anführungszeichen gesetzt, damit im Körper keine Ausdehnungen auftreten. In allen Bourne-alikes kann ich finden, dass dieser den Inhalt wörtlich ausgibt. Wenn ich dasselbe Dokument wie folgt in eine Befehlsersetzung einfüge:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
dann verhalten sie sich nicht mehr identisch:
dash
,ash
,zsh
,ksh93
, BusyBoxash
,mksh
und SunOS 5.10 POSIXsh
alle wörtlich Inhalt des Dokuments geben, wie zuvor.- Bash 3.2 gibt einen Syntaxfehler für ein nicht passendes Backtick aus. Mit übereinstimmenden Backticks wird versucht, den Inhalt als Befehl auszuführen.
- Bash 4.3 reduziert "ghi" und "jkl" auf eine einzelne Zeile, hat aber keinen Fehler. Die
--posix
Option hat darauf keinen Einfluss. Kusalananda sagt mir (danke!), Dass espdksh
sich genauso verhält .
In der ursprünglichen Frage sagte ich, dies sei ein Fehler in Bashs Parser. Ist es? [Update: yes ] Der relevante Text von POSIX (alles aus der Shell Command Language-Definition), den ich finden kann, ist:
- §2.6.3 Befehlsersetzung :
Bei der $ (Befehls-) Form bilden alle Zeichen, die auf die offene Klammer und die entsprechende schließende Klammer folgen, den Befehl. Für den Befehl kann ein beliebiges gültiges Shell-Skript verwendet werden , mit Ausnahme eines Skripts, das ausschließlich aus Umleitungen besteht und nicht angegebene Ergebnisse liefert.
- §2.7.4 Hier-Dokument :
Wenn ein Teil eines Wortes in Anführungszeichen steht, wird das Trennzeichen durch Entfernen von Anführungszeichen für ein Wort gebildet , und die Zeilen in diesem Dokument werden nicht erweitert.
- §2.2.1 Escape-Zeichen (Backslash) :
Wenn dem <Backslash> eine <Newline> folgt, interpretiert die Shell dies als Zeilenfortsetzung. Der <Backslash> und der <Newline> werden entfernt, bevor die Eingabe in Token aufgeteilt wird.
- §2.3 Tokenerkennung :
Wenn ein io_here- Token von der Grammatik erkannt wurde (siehe Shell-Grammatik ), bilden eine oder mehrere der folgenden Zeilen, die unmittelbar auf das nächste NEWLINE- Token folgen , den Hauptteil eines oder mehrerer Here-Dokumente und werden gemäß den Regeln von Here- Dokument .
Wenn keine io_here verarbeitet wird, teilt die Shell ihre Eingabe in Token auf, indem die erste anwendbare Regel unten auf das nächste Zeichen in ihrer Eingabe angewendet wird. ...
...
- Wenn das aktuelle Zeichen ein <Backslash>, ein einfaches Anführungszeichen oder ein doppeltes Anführungszeichen ist und nicht in Anführungszeichen gesetzt wird, wirkt es sich auf die Anführungszeichen bis zum Ende des zitierten Textes aus. Die Regeln für zitiert werden , wie beschrieben in Zitiert . Während der Tokenerkennung werden keine Substitutionen tatsächlich ausgeführt, und das Ergebnistoken muss genau die Zeichen enthalten, die in der Eingabe (mit Ausnahme des <newline> -Zusammenfügens) angezeigt werden, und zwar unverändert, einschließlich eingebetteter oder eingeschlossener Anführungszeichen oder Substitutionsoperatoren zwischen dem und dem Ende des zitierten Textes.
Meine Interpretation ist, dass alle Zeichen nach $(
dem Beenden )
das Shell-Skript enthalten, wörtlich; Es wird ein Here-Dokument angezeigt, sodass die Here-Dokument-Verarbeitung anstelle der normalen Tokenisierung erfolgt. Das hier angegebene Dokument hat dann einen Begrenzer in Anführungszeichen, was bedeutet, dass sein Inhalt wörtlich verarbeitet wird. und der Fluchtcharakter kommt nie hinein. Ich sehe jedoch ein Argument, dass dieser Fall einfach nicht angesprochen wird und beide Verhaltensweisen zulässig sind. Möglicherweise habe ich auch irgendwo einen relevanten Text übersprungen.
- Wird diese Situation anderswo klarer?
- Worauf sollte sich ein portables Skript (theoretisch) verlassen können?
- Ist die spezifische Behandlung einer dieser Shells (Bash 3.2 / Bash 4.3 / alle anderen) durch die Norm vorgeschrieben? Verboten? Zulässig?
echo "$x"
, aber jede Art der Überprüfung der Variablen funktioniert. Ich habe diese Zeile unten bearbeitet.
$(...)
durch die Ausgabe ersetzt ... Wenn Sie den Befehl in Ihrem Beispiel in einer Subshell (in bash
) ausführen , wird das erwartete Ergebnis ausgegeben. Nur wenn es in eine Befehlsersetzung umgewandelt wird, werden "ghi" und "jkl" kollabiert. Das ist also ein Bug imo