Was benötigt POSIX für die hier aufgeführten Dokumente innerhalb der Befehlsersetzung?

20

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, BusyBox ash, mkshund SunOS 5.10 POSIX shalle 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 --posixOption hat darauf keinen Einfluss. Kusalananda sagt mir (danke!), Dass es pdkshsich 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. ...

    ...

    1. 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?
Michael Homer
quelle
Können Sie uns zeigen, wie Sie im zweiten Fall Ihre Ausgabe produzieren?
Julie Pelletier
@ JuliePelletier echo "$x", aber jede Art der Überprüfung der Variablen funktioniert. Ich habe diese Zeile unten bearbeitet.
Michael Homer
2
Sieht aus wie es eine einfache Lösung ist. Dieser Patch scheint zumindest zu funktionieren: ignore_quoted_newline_in_quoted_heredoc.patch
geirha
1
Ich denke, Sie interpretieren dies richtig und imo ist der Standard ziemlich klar, da "Die Shell soll die Befehlsersetzung erweitern, indem sie den Befehl in einer Subshell-Umgebung ausführt und [...] die Befehlsersetzung durch die Standardausgabe von ersetzt the command [...] " Also wird der Befehl in einer Subshell ausgeführt und $(...)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
don_crissti
2
@geirha Ich habe einen Bash-Fehler gemeldet . Ich werde mich nicht um pdksh kümmern, da es nicht einmal einen Schatten der aktuellen Wartung zu haben scheint.
Michael Homer

Antworten:

5

Dies wurde auf der Mailingliste von Bash abgefragt, und der Betreuer bestätigte, dass es sich um einen Fehler handelte

Sie erwähnten auch, dass der Text in POSIX "nicht unbedingt mehrdeutig ist, aber eine genaue Lektüre erfordert.", Also bat ich um eine Klarstellung. Ihre Antwort einschließlich einer Beschreibung des Problems und der Interpretation des Standards lautete wie folgt:

Die Befehlsersetzung ist ein roter Hering; es ist nur insofern relevant, als es darauf hinwies, wo der Fehler war.

Das Trennzeichen zum Here-Dokument wird in Anführungszeichen gesetzt, damit die Zeilen nicht erweitert werden. In diesem Fall liest die Shell Zeilen aus der Eingabe, als wären sie in Anführungszeichen gesetzt. Wenn ein Backslash in einem Kontext erscheint, in dem er in Anführungszeichen steht, fungiert er nicht als Escape-Zeichen (siehe unten), und die spezielle Behandlung von Backslash-Newline findet nicht statt. Wenn ein Teil des Begrenzers in Anführungszeichen gesetzt ist, werden die Zeilen hier im Dokument wie in Anführungszeichen gesetzt gelesen.

Der Text in Posix 2.2.1 ist umständlich geschrieben, bedeutet jedoch, dass der Backslash nur dann speziell behandelt wird, wenn er nicht in Anführungszeichen steht. Sie können einen Backslash zitieren und alle Erweiterungen nur mit einfachen Anführungszeichen oder einem anderen Backslash verhindern.

Der enge Leseteil ist der "nicht erweiterte" Text, der die einfachen Anführungszeichen impliziert. Die Norm sagt in 2.2, dass Dokumente hier "eine andere Form des Zitierens" sind, aber die einzige Form des Zitierens, in der Wörter überhaupt nicht erweitert werden, sind einfache Anführungszeichen. Es ist also eine Form des Zitierens, die genau wie einfache Anführungszeichen ist, jedoch keine einfachen Anführungszeichen.

Kevin
quelle
@Scott (1) Ich glaube, das beantwortet alle Fragen und nichts ist überflüssig. Mein Kommentar, der die Antwort auslöst, bezieht sich auf eine Löschung durch einen Moderator, der die Situation missverstanden hat. (2) Ich habe nicht genug Ruf. (3) Wenn ich meine Antworten gelöscht hätte, hätte ich mir ein ähnliches Verhalten gewünscht, aber ich werde dies in Zukunft sicherlich berücksichtigen. Danke für die Gedanken.
Kevin
Mein Punkt war, dass der größte Teil Ihres ersten Absatzes ein Gespräch mit Michael Mrozek ist und keine Antwort auf die Frage. Mir ist klar, dass Sie nicht genug Reputation haben, um einen Beitrag zu kommentieren, aber ich glaube, dass Sie genug für Meta und Chat haben.
Scott
1
@Scott Ich verstehe und schätze, dass Sie versuchen, die Antwort zu optimieren, aber ich habe diese genau optimierte Antwort zuvor gepostet (nur das Zitat und einen Link dazu) und sie wurde von dem Moderator (ohne Diskussion!) Und mir gelöscht In dem gelöschten Beitrag werden keine Links angezeigt, um zu chatten und diese Entscheidung zu bestreiten. Ich hoffte, dass die Beantwortung seiner unbegründeten Kritik die Löschung überstehen würde, vom Fragesteller akzeptiert würde und dann die Antwort modifizieren würde, um die Präambel zu entfernen.
Kevin