Warum ist die Reihenfolge bei der Ausführung dieser Bash-Befehle wichtig?

10

Es scheint einige Inkonsistenzen zu geben, die ich in Bezug auf die Bash-Shell nicht verstehen kann.

Wenn ich ausführe:

ls;date;time

Die Ergebnisse der drei Abfragen werden nacheinander angezeigt.

Beim Vertauschen von Datum und Uhrzeit wird jedoch eine Fehlermeldung angezeigt.

Also, wenn ich ausführe:

ls;time;date

Die Fehlermeldung lautet : bash: syntax error near unexpected token 'date'.

Kann jemand das erklären?

Rohitvijaysharma
quelle
Ihr Problem liegt auf time;datevs date;time. Dies scheint ein Problem mit der Pipeline bashund dem zuletzt mit der timeAusgabe generierten Zeichen zu sein. Getestete Ergebnisse in verschiedenen Terminalemulatoren sind: - [Bash] $ Datum; Zeit # [OK] $ Zeit; Datum # [ NotOK ] Bash: Syntaxfehler in der Nähe des unerwarteten Tokens "Datum" $ Zeit # Nur Fehler erscheint nicht, dass es das ist Ergebnis eines beliebigen Datums. - [Csh] $ Datum; Uhrzeit # [OK] $ Uhrzeit; Datum # [OK] - [Tcsh] $ Datum; Uhrzeit # [OK] $ Uhrzeit; Datum # [OK] - [Ksh] $ Datum; Uhrzeit # [ OK] $ Zeit; Datum # [OK]
Mostafa Shahverdy
Ich habe meine Antwort mit einer Erklärung für die Fehlermeldung aktualisiert. Bitte überprüfen Sie, ob dies die Antwort ist, nach der Sie gesucht haben.
zwets

Antworten:

10

Der timeBefehl in Ihrer Pipeline ist nicht die /usr/bin/timeBinärdatei, sondern die integrierte Bash time. Vergleiche man timemit help time. Der Fehler, den Sie sehen, ist, dass bash das timeArgument nicht analysiert . Dies muss entweder vorhanden sein oder eine neue Zeile sein. Es ist eine neue Zeile in Ihrem ersten Beispiel, fehlt aber im zweiten.

Auf der anderen Seite, wenn Sie rennen würden

ls;date;'time'

oder

ls;'time';date

Wenn die Anführungszeichen 'time'den Status als reserviertes Wort widerrufen, hat bash keine Probleme, die Zeile zu analysieren. Es analysiert nun drei Befehle in einer Liste, die nacheinander ausgeführt werden, und /usr/bin/timemeldet in beiden Fällen einen Verwendungsfehler.

Nachtrag

Es wurde beobachtet, dass dies zwar time ; dateeinen Fehler ergibt, dies time ; ; datejedoch nicht der Fall ist. Die wahrscheinliche Erklärung ist, dass time ;von Bash als äquivalent zu interpretiert wird time <newline>. Der Ausdruck time ; ; datewird dann als Liste von time ;und analysiert date.

Dies steht im Einklang mit der Beobachtung, dass time ;und time ; ;auch legal sind, wobei die zweite als Singleton-Liste analysiert wird, die time ;das optionale Semikolon enthält, das nach Listen zulässig ist.

Eine andere Möglichkeit zu erklären, warum time ; dateder Fehler auftritt, bash: syntax error near unexpected token 'date'besteht darin, timedas Semikolon zu verbrauchen, von dem er getrennt ist date. Das kann es nur, weil timees ein Bash-reserviertes Wort ist.

zwets
quelle
Danke für eine nette Erklärung! Andererseits sieht dieses Verhalten für mich wie ein Fehler aus: Es timesoll einen NULL-Befehl zulassen, und das Semikolon soll Listen abgrenzen, sodass der timeBefehl IMO das Semikolon danach nicht "verbrauchen" sollte. Andere eingebaute Befehle (die Argumente annehmen können) zeigen dieses Verhalten nicht.
Arrangieren Sie den
@arrange Die Komplikation ist, dass die Zeit keinen Nullbefehl zulässt (der alles eindeutig gemacht hätte), sondern nur eine neue Zeile anstelle des Befehls. Also time;dateist in der Tat syntaktisch falsch in jeder Interpretation. Allerdings time ; und time ; ;wäre dann auch illegal. Es kann diskutiert werden, ob timedas Verhalten ein Fehler ist oder nur undokumentiert (es ist intern konsistent), aber ein Fehlerbericht wäre definitiv vorhanden. Wären Sie bereit, es einzureichen?
zwets
Nun, ich habe mir die Quelle angesehen (bash4.2: parse.y: lines 1205-1221) und dort steht das time by itself can time a null commandund dann tut es das durch $$ = make_simple_command (x, (COMMAND *)NULL);. Was das Einreichen eines Fehlers
arrangiere den
Es sollte beachtet werden, dass dieses Problem bash-spezifisch ist. Dies time ; datefunktioniert in ksh93und mkshohne Fehler, obwohl ksh es ein timeSchlüsselwort gibt.
Sergiy Kolodyazhnyy
2

Bash behandelt das Integrierte timeals Sonderfall beim Parsen von Befehlszeilen.

Wie in der Bash-Manpage zu lesen ist, wird die eingegebene Zeile zunächst in eine Liste aufgeteilt:

pipeline ; pipeline

wo sich eine Pipeline befindet:

[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

oder in unserem Fall einfach:

time command

dh wenn Zeit vorhanden ist, muss auch ein Befehl vorhanden sein.

[Es gibt einen Sonderfall, timein dem eine neue Zeile folgen kann, der hier jedoch nicht gilt.]

In unserem Fall haben wir also:

time;date

in zwei Pipelines aufgeteilt werden:

1. time
2. date

und Pipeline 1 ist nicht gut ausgebildet, da wir timeohne Befehl haben. Daher der Fehler.

Beachten Sie, dass die Befehlszeile auch timehier nicht funktioniert:

$ /usr/bin/time;date
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]

bash analysiert dies wie erwartet in 2 Pipelines:

1. /usr/bin/time
2. date

und /usr/bin/timeweigert sich dann, ohne Argument zu laufen. Beachten Sie, dass dies ein Fehler ist, /usr/bin/timenicht ein Fehler von bash.

Der Grund dafür, dass Back-Tick funktioniert, ist, dass das Back-Tick nicht timemehr als spezielles Element in der Pipeline interpretiert wird.

dh mit dem Back-Tick:

`time`;date

Es wird als zwei Pipelines analysiert:

1. `time`
2. date

Denken Sie daran, dass eine Pipeline in unserem Fall wie folgt lautet:

[time] command

und das problem war anfangs, dass wir timeohne befehl hatten, was nicht erlaubt ist. Aber jetzt haben wir einfach den Befehl:

`time`

ohne das Vorhergehende time, da die Back-Ticks bedeuten, dass dies timeals Befehl und nicht als Vorgängerwort interpretiert wird.

Bash führt dann sein eingebautes System timeohne Argumente aus, was akzeptiert wird. Es wird keine Ausgabe erzeugt, und wir sehen keinen Fehler.

Beachten Sie, dass:

`time`

führt tatsächlich das Ergebnis des timeeingebauten Systems aus, dh es wird ausgeführt, was auch immer das timeeingebaute auf stdout erzeugt. Aber da timealleine nichts zu stdout schreibt, scheint es zu funktionieren.

Schließlich wurde festgestellt, dass dies funktioniert:

time ; ; date

was ich leider nicht erklären kann :)

cdmackay
quelle
Ich denke, deine Erklärung ist besser, aber sie sieht für mich immer noch komisch aus. ;dategibt bash: syntax error near unexpected token ;, aber time ;dategibt bash: syntax error near unexpected token date, so dass es scheint, dass bash den Befehl nach der eingebauten Zeit nicht als "; Datum" behandelt. Interessanterweise time ; ; datefunktioniert.
arrangieren Sie den
yup, danke @arrange, es ist ziemlich komisch. Ich werde die Antwort leicht aktualisieren.
cdmackay
ok, @arrange, habe umgeschrieben. Ich kann dein letztes aber immer noch nicht erklären ... seufz.
cdmackay
@cdmackay Sie verwechseln Backticks und Anführungszeichen. Durch das Zitieren 'time' verliert es seine Bedeutung als reserviertes Wort. Wenn Sie es zurückticken , wird es in einer Subshell ausgeführt, deren Ausgabe in den Befehl eingefügt wird. Dies hat nichts mit der Diskussion zu tun. In der Tat `time\';datebeweist Ihr Beispiel das Gegenteil Ihrer Behauptung: Dies sollte durch Ihre Argumentation einen Fehler ergeben, da /usr/bin/timeein Argument erforderlich ist. Der Grund dafür ist, dass es sich in der Subshell, in der es ausgeführt wird, wieder um das reservierte Wort handelt time.
zwets
@arrange Bei beiden handelt es sich um Syntaxfehler, und es wird berichtet, dass sich beide in der Nähe derselben Stelle befinden, sodass ich dort keine Inkonsistenz sehe. Sobald es in das Land der Syntaxfehler eingeht, können Sie nicht erwarten, dass ein Parser seinen Ausweg kennt. Wenn Sie die eines Parsers benötigen, muss dieser nicht nur die legale Syntax kennen, sondern auch die Syntax jedes möglichen illegalen Konstrukts, was per Definition unmöglich ist.
zwets