Warum ist in ksh ein Leerzeichen zwischen "[[" und "-e xxx" erforderlich?

9

Der folgende Befehl funktioniert beispielsweise nicht:

if [[-e xyz]]; then echo File exists;fi

ksh gibt den folgenden Fehler aus

[[-e: command not found

Liegt das daran, dass "[[-" nicht eindeutig ist?

Myloti
quelle
2
[[ist ein Schlüsselwort - vermutlich erfordert der
Analysebaum
Eine andere Sichtweise ist wahrscheinlich, dass Sie eine Funktion schreiben könnten [[- -, wie würde die Shell wissen, ob sie "do the e flag on [[" anstelle von "do the function [[- on e" "analysiert.
pbhj

Antworten:

15

Die einfachste Erklärung wäre, weil es im Handbuch so aussieht, als müsste [[ expression ]]zwischen [[und expressionund Schließen ein Leerzeichen sein ]]. Aber natürlich können wir versuchen, es genauer zu betrachten. Muscheln haben eine etwas komplexe Grammatik und stützen sich stark auf das Konzept der "Wortteilung". Im Handbuch von ksh (1) heißt es insbesondere:

Die Shell beginnt, ihre Eingabe zu analysieren, indem sie sie in Wörter zerlegt. Wörter, bei denen es sich um Zeichenfolgen handelt, werden durch nicht zitierte Leerzeichen (Leerzeichen, Tabulator und Zeilenumbruch) oder Metazeichen (<,>, |,;, &, (und)) begrenzt. Abgesehen von der Begrenzung von Wörtern werden Leerzeichen und Tabulatoren ignoriert, während Zeilenumbrüche normalerweise Befehle begrenzen.

Wie im Handbuch angegeben, wird die Zeichenfolge [[-eals Shell-Wort betrachtet. kshwürde nach einem solchen Befehl in der Liste der eingebauten und speziellen Operatoren (wie foroder while) suchen und dann nach einem externen Befehl suchen - und voilà - keiner gefunden, daher gibt es eine Fehlermeldung über den Befehl nicht gefunden.

In diesem Fall handelt [[es sich auch nicht um spezielle Metazeichen. Wenn dies der Fall wäre, würde der -eTeil in [[-eals Shell-Wort betrachtet, nicht als Argument für [[sich. Wird [[jedoch als zusammengesetzter Befehl beschrieben, und das Handbuch sagt Folgendes aus:

Zusammengesetzte Befehle werden mit den folgenden reservierten Wörtern erstellt. Diese Wörter werden nur erkannt, wenn sie nicht in Anführungszeichen stehen und als erstes Wort eines Befehls verwendet werden (dh ihnen können keine Parameterzuweisungen oder Umleitungen vorangestellt werden):

Um [[als zusammengesetzter Befehl erkannt zu werden, muss es ein erstes Wort eines Befehls oder einer Befehlsliste sein, was nach vorheriger Definition eines "Wortes" impliziert, dass es durch Leerzeichen, Tabulatoren oder Zeilenumbrüche von anderen getrennt werden muss Wörter / Argumente.


Sie haben in den Kommentaren gefragt : "Warum kann der Parser nicht anhalten, sobald er das Muster" [["findet und dies als Beginn eines bedingten Befehls behandelt?" Eine kurze Antwort ist wahrscheinlich, weil 1) der Einfluss der [Syntax, da es sich ursprünglich um einen externen Befehl handelte, und für POSIX-Standards die Konformität auch heute noch als externer Befehl besteht, und 2) weil der Shell-Parser so aufgebaut ist. Der Shell-Parser kann andere nicht durch Leerzeichen getrennte Sonderzeichen erkennen echo $((2+2))und (echo foobar)funktioniert einwandfrei. Vielleicht wird in Zukunft, wenn die kshEntwicklung wieder aufgenommen wird oder es einen Fork oder Klon (wie mkshoder pdksh) gibt, jemand eine platzlose Syntax implementieren [[-e.

Siehe auch:

Sergiy Kolodyazhnyy
quelle
3
Ich denke, das manuelle Zitat von ksh ist wirklich vor Ort. Es ähnelt jeder anderen Programmiersprache: In C charist es ein Schlüsselwort, aber Sie können immer noch nicht schreiben charsomeletter = 'A';und erwarten , dass der Parser nach dem Anzeigen stoppt char.
Peter - Monica am
Ich denke, der Hauptunterschied zwischen Ihrem Beispiel für "char" und dem Symbol "[[" in der Frage besteht darin, dass ein alphanumerischer Bezeichner im Allgemeinen ein Leerzeichen benötigt, um sich von anderen zu trennen. Spezielle Symbole als Token benötigen dagegen keine Leerzeichen.
Myloti
Genau. Ich bin mir nicht sicher, ob der Vergleich mit C hilfreich ist. In C kann man sagen i--<=++j, und der Compiler hat kein Problem damit, das als zu analysieren i -- <= ++ j.
Scott
@Scott Ich denke, der C-Vergleich ist hilfreich (und dass es irgendwie sekundär ist, dass C-Bezeichner nur alphanumerische Zeichen zulassen und _). Ksh hat (als Operator und (echo hello)analysiert als ( echo hello ). Es hat sich if, {, [[und andere Schlüsselwörter , und ifx, {xund [[xjeweils ein Token - wie , wie charsomeletterist ein C - Token. Im Gegensatz dazu besteht ein (xin ksh nicht zitiertes Zeichen aus zwei Token - genau wie i--zwei Token in C. Was es konzeptionell sogar bedeutet , ein Operator zu sein, unterscheidet sich zwischen Bourne-Shells (wie ksh) und C. Peter A. Schneider wies jedoch auf a hin echte lexikalische Ähnlichkeit.
Eliah Kagan
2

Es ist wichtig zu beachten, dass [es sich um einen Befehl handelt und das Shell-Schlüsselwort [[in bash und ksh auf basiert [.

[[wird in ähnlicher Weise verwendet wie [. Manchmal kann man sogar ersetzen [ ]mit [[ ]]ohne Änderung im Verhalten. Mit [wie jeder Befehl ist ein Raum zwischen dem Befehl und seine Argumente obligatorisch. Gleiches gilt für [[.

Früher hatten wir nur /usr/bin/[. Jetzt haben [die meisten Shells aus Effizienzgründen eingebaut - aber die Syntax ist dieselbe. In Muscheln, die bieten [[, fungiert es als vielseitigere Alternative zu [.

Hier ist die Beschreibung für [in bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Ist [also gleichbedeutend mit test(abgesehen davon, dass ]am Ende ein Argument erwartet wird ). help testwird noch mehr Details dazu geben. Sie können dies mit vergleichen help [[.

Es gibt auch eine Manpage für den externen Befehl [( man \[).

Im Falle des

if [[-e
  • Weder [noch [[ist ein Befehl da. Das ganze Wort [[-eist.
  • Das if [[-emacht es zu einem Test für wahr / falsch. Existiert also ein Befehl [[-e?

Liegt das daran, dass "[[-" nicht eindeutig ist?

Ja. Oder Nein. [[-eist, was es ist: nichts, was die Shell versteht, also nimmt sie an, dass es ihr eigener Befehl ist. ;-);

Rinzwind
quelle
"% help [[" sagt, dass "[[" ein bedingter Befehl ist. Warum kann der Parser nicht anhalten, sobald er das Muster "[[" findet und dies als Start eines bedingten Befehls behandelt?
Myloti
@Myloti [[-eist ein potenziell gültiger (wenn auch seltsam aussehender) Befehlsname.
Gordon Davisson
2

Der Raum ist ein Begrenzer und wird benötigt. Wie Sie sehen können aus shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Sowohl ksh als auch bash unterstützen [[und funktionieren nicht ohne Leerzeichen. Shellcheck liefert genau diese Ausgabe mit ähnlichen ksh- und bash-Skripten, die diese fehlerhafte Zeile enthalten.)

Warum Deliminatoren benötigt werden, hat mit Token und Lexika zu tun .

WinEunuuchs2Unix
quelle
Bitte beachten Sie, dass OP kshin der Frage nach Shell gefragt hat. Bash leiht [[Operator von kshund in dieser Frage ist ihr Verhalten identisch, aber ich würde vorsichtig mit Annahmen sein, da es einige signifikante Unterschiede zwischen kshund bashVerhalten und Interna gibt. Das liegt nur daran, dass Shellcheck mit Bash-Skripten funktioniert. Es ist möglicherweise nicht unbedingt das geeignete Tool für Ksh-Skripte. Nur etwas zu beachten, wenn Sie sich verschiedenen Muscheln nähern
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Ich war opportunistisch und habe Shellcheck verwendet, um die [[integrierten Regeln hervorzuheben . Ich habe in keiner Weise gefolgert, dass kshes sich um eine Shell handelt, shellcheckin der Fehler gefunden werden können. Ich hoffe, andere schätzen kshund bashsind zwei verschiedene Interpreten. Danke, dass Sie das erwähnt haben. Erwähnenswert [[ist auch eine eingebaute Bash, während [es sich um einen externen Befehl handelt.
WinEunuuchs2Unix
Eigentlich [ist auch eine eingebaute Bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Aber Sie sind nicht weit weg - [oder testmüssen ein externer Befehl sein. In den Tagen des ursprünglichen Bourne war die Shell [tatsächlich ein externer Befehl und existiert heute noch, /usr/bin/[da POSIX einen externen Befehl benötigt. Pubs.opengroup.org/onlinepubs/009695399/utilities/test.html POSIX benötigt nur sehr wenige eingebaut sein pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Eingebaut [ist für Effizienz
Sergiy Kolodyazhnyy
2
Shellcheck weiß es ksh; benutze einen Hashbang oder -s ksh. Übrigens haben ksh und bash [["eingebaut", aber es ist ein Schlüsselwort , im Gegensatz zu [einer eingebauten Shell . (ksh : whence -v [ [[; bash type [ [[:) Builtins und externe Befehle sind syntaktisch gleich. Ein Weg, der sich davon [[unterscheidet, [besteht darin, dass [[einige Erweiterungen in seinen Argumenten unterdrückt werden, die als eingebautes Element nicht möglich waren - ebenso wenig wie eine {Gruppierung, wenn es ein eingebautes Element wäre. Dies kann der Grund sein, warum {x(oder [[x) es sich seltsam anfühlt, ein Token zu sein. Ich denke, es ist richtig zu sagen, dass Builtins und Keywords lexikalisch, aber nicht syntaktisch ähnlich sind. @SergiyKolodyazhnyy
Eliah Kagan
1
@EliahKagan Eigentlich habe ich eine Frage zu U & L gestellt, um eine genaue Antwort darauf zu erhalten, was POSIX über diese Situation denkt. Unix.stackexchange.com/questions/526574/… Die Shell-Sprache ist komplex genug, sodass hoffentlich jemand sie formeller erklären kann
Sergiy Kolodyazhnyy
1

[[kann ein externer Befehl sein! Das heißt, es kann sich um ein Programm handeln und nicht um eine Syntax, die von Ihrer Shell direkt unterstützt wird. Die Unterstützung einer platzlosen Syntax wäre möglich ksh, würde jedoch auf Systemen mit externem System fehlschlagen. Aus [[Kompatibilitätsgründen ist es daher besser, den erforderlichen Speicherplatz beizubehalten.

BusyBox stellt [[beispielsweise einen externen Befehl bereit.

DarkDust
quelle
0

Da [[-ekann an der Shell-Erweiterung teilnehmen (es kann wie verwendet werden

echo [[-e]*

um alle Dateien beginnend mit einem Buchstaben zwischen aufzulisten [und eeinschließend), wäre es ein komplettes Chaos, wenn [und ]wurden Sonderzeichen nicht in der normalen Leer regierten Wort Spaltung beteiligt.


quelle