Post by Jan NovakEin
printf '%s\n' {"test":"bla"}
gibt
{test:bla}
aus. Das ist kein json String mehr. Die doppelten Anführungszeichen fehlen.
Das hat damit zu tun, dass der Shell gewisse Zeichen in der
Kommandozeile nicht einfach als Parameter stehen lässt, sondern ihnen
eine besondere Bedeutung zugewiesen hat und sie besonders behandelt.
Wenn man die Sonderbehandlung nicht haben möchte, sondern die Zeichen
einfach für sich stehen sollen, muss man sie durch Quoting (s. u.)
davor schützen (siehe
<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02>).
Zu diesen Sonderzeichen gehören die Zeichen
| & ; < > ( ) $ ` \ " ' <space> <tab> <newline>
und in manchen Zusammenhängen auch noch die Zeichen
* ? [ # ˜ = %
Moderne Shells haben möglicherweise noch weitere Zeichen; beim Bash
beispielsweise gehören die Schweifklammern in bestimmten Fällen auch
noch dazu.
Für das Quoting stellt der Shell verschiedene Mechanismen, die alle
ihre Vor‐ und Nachteile haben, bereit:
Der erste funktioniert mit dem „\“ (backslash, umgekehrter
Schrägstrich, siehe
<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01>):
Ein Backslash, der seinerseits nicht durch einen Quoting‐Mechanismus
vor Sonderbehandlung geschützt ist, hat folgende Wirkung: Er
schützt das nachfolgende Zeichen, sofern es kein Newline‐Zeichen
ist, vor dessen Sonderbedeutung. Auch Zeichen, die keine Sonderbedeutung
haben, dürfen mit einem Backslash geschützt werden. Das ist dann
zwar unnötig, schadet aber nicht.
Beispiel:
Um den Text „{"test":"bla"}“ in einer Zeile auszugeben, kann man
printf '%s\n' \{\"\t\e\s\t\"\:\"\b\l\a\"\}
schreiben. Dabei ist das Quoting zumindest bei den Buchstaben
unnötig, schadet aber nicht.
Selbst ein „\“ kann mit einem „\“ geschützt werden:
printf '%s\n' \\
gibt einen Apostroph in einer Zeile aus.
Und dann hat dieser Mechanismus noch eine Besonderheit: Wenn man ein
„\“‐Zeichen unmittelbar vor ein Newline‐Zeichen – anders ausgedrückt:
ans Zeilenende – stellt, wird das Newline‐Zeichen nicht geschützt
sondern zusammen mit dem „\“ vom Shell ignoriert, genau so, als
ständen beide nicht da. Damit lassen sich lange Kommandozeilen
umbrechen.
Ich halte diesen Mechanismus für massenhafte Anwendung von Hand für
nicht so praktikabel, weil es nicht sicher ist, welche Zeichen das
Quoting nötig haben. Beispielsweise hat der Bash neben denen, die
ein POSIX‐Shell hat, noch weitere.
\D\i\e\ \A\l\t\e\r\n\a\t\i\v\e\,\ \e\i\n\f\a\c\h\ \a\l\l\e\ \
\Z\e\i\c\h\e\n\ \d\a\m\i\t\ \z\u\ \s\c\h\ü\t\z\e\n\, \
\f\u\n\k\t\i\o\n\i\e\r\t\ \z\w\a\r\,\ \i\s\t\ \a\b\e\r\ \i\n\ \
\m\a\s\s\e\n\h\a\f\t\e\r\ \A\n\w\e\n\d\u\n\g\ \n\i\c\h\t\ \
\s\e\h\r\ \g\u\t \z\u\ \l\e\s\e\n\.
Deshalb, und weil man mit dem Backslash ein Newline‐Zeichen nicht
schützen kann, gibt es als zweiten Quoting‐Mechanismus den
Mechanismus mit dem Apostroph (siehe
<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_02>).
Der funktioniert so:
Alle Folgen von Zeichen, die man zwischen zwei Apostrophe
einschließt, verlieren ihre Sonderbedeutung und stehen für sich. Das
bedeutet natürlich, dass man einen Apostroph nicht zwischen zwei
Apostrophe einschließen kann (denn der eingeschlossene Apostroph
würde statt dessen die Einschließung beenden).
printf '%s\n' '{"test":"bla"}'
gibt den Text „{"test":"bla"}“ in einer Zeile aus.
Und dann gibt es noch einen dritten Quoting‐Mechanismus, den
Mechanismus mit dem Anführungszeichen (siehe
<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_03>).
Der funktioniert so ähnlich wie der mit dem Apostroph, hat aber ein
paar Besonderheiten:
Die wichtigste ist, dass darin vorkommende Parameter‐ oder
Variablenexpansionen („$1“ oder „$variable“) oder
Kommandoausgabe‐Substitutionen
„$(ein Kommando)“
und
„`ein Kommando`“
ausgeführt werden, ohne dass das Resultat anschließend noch an
IFS‐Bruchstellen zerbrochen wird. Auch „\“‐Zeichen haben darin eine
Sonderbedeutung, die der vom ersten Quoting‐Mechanismus ähnelt, aber
nicht gleich ist. Genaueres steht im angeführten URL.
Als Merkregel halte ich für mich fest: Außer für Parameter‐ oder
Variablenexpansionen und Kommandoausgabe‐Substitutionen (und
vielleicht noch, um einem Apostroph seine Sonderbedeutung zu nehmen)
ziehe ich ihm die beiden anderen Quoting‐Mechanismen vor.
Was macht man nun, wenn man beispielsweise den Text
Konrad sprach zur Frau Mama:
"Ich geh' fort und Du bleibst da."
(siehe
<http://www.dasbergwerk.de/Niederer_Unfug/Struwelpeter.html>)
ausgeben möchte?
Die letzte Gedichtzeile macht Ärger: Sie einfach in Apostrophe
einzuschließen, funktioniert wegen dem Wort „geh'“ nicht:
printf '%s\n' \
'Konrad sprach zur Frau Mama:' \
'"Ich geh' fort und Du bleibst da."'
Sie in Anführungszeichen zu setzen, funktioniert auch nicht, wegen
der Anführungszeichen für die wörtliche Rede:
printf '%s\n' \
'Konrad sprach zur Frau Mama:' \
""Ich geh' fort und Du bleibst da.""
Woran man vielleicht zunächst nicht denkt, was im Zusammenhang mit
Quoting aber sehr hilfreich ist:
Man kann in einer Kommandozeile jederzeit zwei (egal, auf welche
Weise) gequotete oder ungequotete Textstücke aneinanderkleben
(ohne dass die dann aneinanderstoßenden Quotingmechanismen
einander stören), um ein Textstück, das aus der Zusammenklebung
der beiden Teile besteht, zu erhalten. Man braucht sie dazu nur
ohne Zwischenraum nebeneinanderzusetzen:
Beispiel:
Im Kommando
printf '%s\n' 'Hello, world!'
kann der Parameter „Hello, world!“ beispielsweise auch als
Zusammenklebung des ungequoteten Stücks „Hel“ und des in Apostrophe
eingefassten Stücks „lo, world!“ geschrieben werden:
printf '%s\n' Hel'lo, world!'
=> Die Aneinanderklebemethode erlaubt es, jederzeit den
Quotingmechanismus zu wechseln.
Das kann man nutzen, um in einem Text, der einen Apostroph
enthält, alle Zeichen außer dem Apostroph zu quoten: Man beginnt
den Text in Apostrophe eingefasst, beendet unmittelbar vor dem im
Text enthaltenen Apostroph die Einfassung in Apostrophe, fasst dann
den gewünschten Apostroph entweder in „""“ ein oder maskiert ihn durch
ein vorangestelltes „\“ und kann danach mit der Einfassung in
Apostrophe fortfahren:
Beispiel:
printf '%s\n' \
'Konrad sprach zur Frau Mama:' \
'"Ich geh'"'"' fort und Du bleibst da."'
| ^^^^^
| |\|/4.: mit Einfassung in Apostrophe fortfahren
| | 3.: einen Apostroph in Anführungszeichen einfassen
| 2.: die Einfassung in Apostrophe beenden
1.: eine Einfassung in Apostrophe beginnen
oder auch:
printf '%s\n' \
'Konrad sprach zur Frau Mama:' \
'"Ich geh'\'' fort und Du bleibst da."'
| ^^ ^
| || 4.: mit Einfassung in Apostrophe fortfahren
| |3.: den Apostroph mit einem Backslash schützen
| 2.: die Einfassung in Apostrophe beenden
1.: eine Einfassung in Apostrophe beginnen