Warum wird bei Metazeichen manchmal ein Leerzeichen benötigt?

543

Vor ein paar Monaten habe ich eine Gabelbombe auf meinen Arm tätowiert und die Leerzeichen übersprungen, weil ich denke, dass es ohne sie schöner aussieht. Aber zu meiner Bestürzung startet es manchmal (nicht immer), wenn ich es in einer Shell laufen lasse, keine Gabelbombe, sondern es gibt nur einen Syntaxfehler.

bash: syntax error near unexpected token `{:'

Gestern passierte es, als ich versuchte, es in der Bash- Shell eines Freundes auszuführen , und dann fügte ich das Leerzeichen hinzu, und es funktionierte plötzlich :(){ :|:& };:statt:(){:|:&};:

Ist das Leerzeichen wichtig? Habe ich einen Syntaxfehler auf meinen Arm tätowiert?!

Es scheint immer in zsh zu funktionieren , aber nicht in Bash.

Eine verwandte Frage erklärt nichts über die Leerzeichen, was wirklich meine Frage ist; Warum wird das Leerzeichen benötigt, damit Bash es richtig analysieren kann?

spydon
quelle
6
Ich stellte die gleiche Frage hier ( mit Ausnahme des Tattoo - Teil).
Benoit
3
Außerdem kann der Doppelpunkt (:) nicht als Funktionsname verwendet werden (siehe: pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... FreeBSDs / bin / sh geben sogar einen Fehler in diesem ...
Martin Tournoij
5
@ Carpetsmoker: Ich bin mir nicht sicher, wie das relevant ist. Diese Frage bezieht sich auf Bash.
Dennis

Antworten:

267

Es gibt eine Liste von Zeichen, die Token in BASH trennen. Diese Zeichen werden als Meta - Zeichen , und sie sind |, &, ;, (, ), <, >, Raum und Reiter . Auf der anderen Seite sind geschweifte Klammern ( {und }) nur gewöhnliche Zeichen, aus denen Wörter bestehen.

Das Weglassen des zweiten Leerzeichens reicht aus }, da &es sich um ein Metazeichen handelt. Daher sollte Ihr Tattoo mindestens ein Leerzeichen haben.

:(){ :|:&};:
Dmitri Chubarov
quelle
35
Einfache Lösung: Bewegen Sie den ersten Teil des Taoo nach links und transplantieren Sie die Haut zwischen den beiden Teilen.
23
Ich mochte den Begriff, on the other hand... Wortspiel beabsichtigt? ;): D (Entschuldigung, für den Kommentar zum Thema.)
anishsane
4
Aber das ist anders für zsh? Inwiefern unterscheidet sich zsh?
Tfogo
2
Gute Erklärung, außer dass {und in diesem Zusammenhang Shell-Schlüsselwörter} sind . Nur wenn sie aufgrund des Mangels an umgebenden Räumen / Metazeichen nicht als solche erkannt werden, werden sie als wörtlicher Teil des Wortes behandelt, zu dem sie gehören. (Und dann gibt es eine
Klammererweiterung
2
@DmitriChubarov Klammern sind in Befehlsnamen zulässig (einschließlich Funktionen : {foo} () { echo hello; }. "Name", definiert bashals "ein Wort, das nur aus alphanumerischen Zeichen und Unterstrichen besteht und mit einem alphabetischen Zeichen oder einem Unterstrich beginnt", gilt nur für Variablennamen.
Chepner
82

Einfach tätowieren a

#!/bin/zsh

Shebang darüber und es wird dir gut gehen.

SzG
quelle
6
Wenn wir wählerisch sind, würde der Shebang überhaupt nicht funktionieren, da sich die Shell, hoffentlich zsh, im interaktiven Modus befindet, wie die Eingabeaufforderung zeigt ...
SzG
50

Klammern sind eher ungerade Schlüsselwörter als spezielle Symbole und benötigen Leerzeichen. Dies unterscheidet sich beispielsweise von Klammern. Vergleichen Sie:

(ls)

was funktioniert und:

{ls}

welches nach einem Befehl namens sucht {ls}. Um zu arbeiten, muss es sein:

{ ls; }

Das Semikolon verhindert, dass die schließende Klammer als Parameter für verwendet wird ls.

Alles, was Sie tun müssen, ist den Leuten zu sagen, dass Sie eine Proportionalschrift mit einem ziemlich engen Leerzeichen verwenden.

Peter Westlake
quelle
12
@DmitriChubarov - es ist ein sehr hinterhältiger Trick, bei dem Zahnspangen eine völlig andere Bedeutung haben. Es erweitert die durch Kommas getrennte Werteliste, in diesem Fall nur die ls.
Peter Westlake
7
Aber ... Junge ... du wirst das Tattoo für den Rest deines Lebens haben! Du hast dich für alle Zeiten als Nerd markiert! Und dann konnten Sie es nicht einmal richtig machen, bevor Sie es nähen ließen? Das sind zwei Schritte darüber hinaus, nerdig zu sein, denke ich ;-) Nichts für ungut, Kumpel, ich frage mich nur.
Alfe
14
@Alfe Ich habe es versucht, bevor ich es tätowiert habe, aber ich habe es in meinem zsh versucht. Ich dachte, es hat das gleiche Parsing wie Bash. Dumm von mir, aber ich werde den Leuten nur sagen, dass es zsh ist. :)
Spydon
20
@spydon Oder du sagst ihnen, dass du den Platz absichtlich weggelassen hast, damit Leute, die den Befehl von deinem Tattoo kopieren, ihn nicht versehentlich ausführen und ihre Maschine zum Absturz bringen;)
stupse den
4
Hey, wenn es in zsh gültig ist, warum nicht einfach #! / Bin / zsh direkt darüber (oder davor) hinzufügen? Es ist sowieso empfehlenswert, zuerst die Shell anzugeben.
Adam Miller
41

Obwohl in der Tätowierungsschrift nicht leicht sichtbar, befindet sich zwischen der Klammer und dem Doppelpunkt tatsächlich ein Byte-Order Mark (BOM) (Sie waren möglicherweise ausreichend betrunken, als Sie das Tätowier erhalten haben, dass Sie es nicht bemerkt haben, aber es ist wirklich vorhanden). . Dies lässt drei offensichtliche Möglichkeiten:

  1. Sie haben die Stückliste beim Transkribieren des Codes nicht eingegeben. Das Ergebnis ist eine offensichtliche Anwendung von GIGO. Die Shell erkennt einfach keine Stückliste, die in Ihrer fehlgeschlagenen Transkription nicht vorhanden ist.
  2. Deine Muschel ist zu alt. Unicode-Zeichen werden nicht erkannt, daher wird die Stückliste (und wahrscheinlich alle anderen Unicode-Zeichen) vollständig ignoriert, obwohl eine Stückliste an einer anderen Stelle als am Anfang einer Datei als Leerzeichen mit einer Breite von null behandelt werden soll .
  3. Deine Shell ist zu neu. Die Verwendung einer Stückliste als ZWNBS ist veraltet, und die Autoren haben eine zukünftige Version von Unicode implementiert, in der diese Verwendung nicht mehr zulässig ist.
Jerry Sarg
quelle
40

und dann habe ich das Leerzeichen hinzugefügt und es hat plötzlich funktioniert ...

Es liegt daran, wie die Shell analysiert. Sie benötigen ein Leerzeichen, nachdem die Funktionsdefinition begonnen hat, dh nach dem {.

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

sind gültig. Auf der anderen Seite,

foo() {echo hey&}

ist nicht.


Sie brauchen tatsächlich eine Tätowierung wie diese:

Geben Sie hier die Bildbeschreibung ein


Aus der Quelle :

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

Das Weglassen eines Leerzeichens nach dem {bewirkt {echo, dass das als einzelnes Token interpretiert wird.


Eine äquivalente Form von

:(){ :|:& };:

wäre

:(){
:|:& };:

Beachten Sie, dass {in der alternativen Version kein Leerzeichen mehr vorhanden ist. Durch einen Zeilenumbruch wird die Shell jedoch {als Token erkannt .

devnull
quelle
"Durch das Weglassen eines Leerzeichens nach dem {wird das {Echo als einzelnes Token interpretiert." - Glaubt der Parser also, dass er auf einen Befehl gestoßen ist, der aufgerufen wurde, {echobevor er eine erforderliche Öffnungsklammer erreicht hat?
Jonah