Pourquoi la séquence est-elle importante dans l'exécution de ces commandes bash?

10

Il semble y avoir une incohérence que je ne peux pas comprendre concernant le shell bash.

Si j'exécute:

ls;date;time

les résultats des trois requêtes sont présentés dans l'ordre.

Cependant, lors de l'échange de la date et de l'heure, un message d'erreur apparaît.

Donc, si j'exécute:

ls;time;date

le message d'erreur indique: bash: syntax error near unexpected token 'date'.

Quelqu'un peut-il expliquer cela?

rohitvijaysharma
la source
Votre problème est sur time;datevs date;time. Cela semble être un problème avec le pipeline entrant bashet le dernier caractère généré avec la timesortie. Les résultats testés dans différents émulateurs de terminal sont les suivants: - [Bash] $ date; heure # [OK] $ heure; date # [ NotOK ] bash: erreur de syntaxe près du jeton inattendu `date '$ heure # seule erreur n'apparaît pas qu'il s'agit du résultat de n'importe quelle date. - [Csh] $ date; heure # [OK] $ heure; date # [OK] - [Tcsh] $ date; heure # [OK] $ heure; date # [OK] - [Ksh] $ date; heure # [ OK] $ heure; date # [OK]
Mostafa Shahverdy
J'ai mis à jour ma réponse avec une explication du message d'erreur. Veuillez vérifier que c'est la réponse que vous cherchiez.
zwets

Réponses:

10

La timecommande dans votre pipeline n'est pas le /usr/bin/timebinaire, mais le bash timeintégré. Comparez man timeavec help time. L'erreur que vous voyez est que bash ne parvient pas à analyser timel'argument de. Cela doit être présent ou être une nouvelle ligne. C'est une nouvelle ligne dans votre premier exemple mais absente dans le second.

D'un autre côté, si vous deviez courir

ls;date;'time'

ou

ls;'time';date

où les guillemets autour 'time'révoquent son statut de mot réservé, alors bash n'a aucun problème à analyser la ligne. Il analyse maintenant trois commandes dans une liste, qu'il exécutera en séquence et /usr/bin/timesignalera une erreur d'utilisation dans les deux cas.

Addenda

Il a été observé que, bien que time ; dategénère une erreur, time ; ; datene le fait pas. L'explication probable est que time ;bash est interprété comme équivalent à time <newline>. L'expression time ; ; dateest ensuite analysée comme la liste de time ;et date.

Ceci est cohérent avec l'observation que time ;et time ; ;sont également légaux, le second étant analysé comme la liste singleton contenant time ;suivie du point-virgule facultatif autorisé après les listes.

Donc, une autre façon d'expliquer pourquoi time ; datedonne l'erreur bash: syntax error near unexpected token 'date'est de timeconsommer le point-virgule qui le sépare date. Il ne peut le faire que parce qu'il times'agit d'un mot réservé à bash.

zwets
la source
Merci pour une belle explication! Mais là encore, ce comportement ressemble à un bogue pour moi: timeest censé autoriser une commande NULL, et le point-virgule est censé délimiter les listes, donc IMO la timecommande ne doit pas "consommer" le point-virgule après. Les autres commandes intégrées (qui peuvent prendre des arguments) ne présentent pas ce type de comportement.
arranger
@arrange La complication est que le temps ne permet pas une commande nulle (qui aurait tout désambiguïsé), il ne permet qu'une nouvelle ligne à la place de la commande. Il en time;dateva de même pour la syntaxe dans toute interprétation. Cependant time ; et time ; ;serait alors également illégal. Il peut être débattu si timele comportement de est un bogue ou simplement non documenté (il est cohérent en interne), mais un rapport de bogue serait certainement en place. Seriez-vous prêt à le déposer?
zwets
Eh bien, j'ai regardé la source (bash4.2: parse.y: lignes 1205-1221) et là, il dit cela time by itself can time a null command, puis il le fait par $$ = make_simple_command (x, (COMMAND *)NULL);. Quant au dépôt d'un bug, je ne suis pas sûr 8)
arrangez-vous
A noter que ce problème est spécifique à bash. Faire time ; datefonctionne dans ksh93et mkshsans erreur, même s'il ksh y a un timemot - clé.
Sergiy Kolodyazhnyy
2

Bash traite le intégré timecomme un cas spécial lors de l'analyse des lignes de commande.

Comme on peut le lire dans la page de manuel bash, la ligne telle qu'elle est tapée est d'abord divisée en une liste:

pipeline ; pipeline

où un pipeline est:

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

ou dans notre cas, simplement:

time command

c'est-à-dire si le temps est présent, alors la commande doit également être présente.

[Il y a un cas particulier qui permet timed'être suivi d'une nouvelle ligne, mais cela ne s'applique pas ici]

Donc, dans notre cas, nous avons:

time;date

étant divisé en deux pipelines:

1. time
2. date

et le pipeline 1 n'est pas bien formé, car nous avons timesans commande. D'où l'erreur.

Notez que la ligne de commande timene fonctionne pas non plus ici:

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

bash analyse cela comme prévu, en 2 pipelines:

1. /usr/bin/time
2. date

et /usr/bin/timerefuse ensuite d'exécuter sans argument. Notez que c'est une erreur de /usr/bin/timepas une erreur de bash.

La raison pour laquelle le back-tick fonctionne est que le back-tick cesse d' timeêtre interprété comme un élément spécial dans le pipeline.

c'est à dire avec le back-tick:

`time`;date

il est analysé comme deux pipelines:

1. `time`
2. date

N'oubliez pas qu'un pipeline, dans notre cas, c'est:

[time] command

et le problème était que nous n'avions timepas de commande, ce qui n'est pas autorisé. Mais maintenant, nous avons simplement la commande:

`time`

sans le précédent time, car les tics inversés signifient que timec'est interprété comme la commande, pas comme un mot précédent.

Donc bash exécute alors sa commande interne timesans args, ce qui est accepté. Il ne produit aucune sortie et nous ne voyons aucune erreur.

Notez que:

`time`

exécute en fait le résultat de la fonction timeintégrée, c'est-à-dire qu'il exécute tout ce que la fonction timeintégrée produit sur stdout. Mais comme timeil n'écrit rien à lui seul, il semble fonctionner.

Enfin, il a été noté que cela fonctionne:

time ; ; date

ce que je ne peux pas expliquer, malheureusement :)

cdmackay
la source
Je pense que votre explication est meilleure, mais elle me semble toujours bizarre. ;datedonne bash: syntax error near unexpected token ;, mais time ;datedonne bash: syntax error near unexpected token date, il semble donc que bash ne traite pas la commande après l'heure intégrée comme "; date". Fait intéressant, time ; ; datefonctionne.
organiser
yup, merci @arrange, c'est assez bizarre. Je vais mettre à jour légèrement la réponse.
cdmackay
ok, @arrange, ont réécrit. Je ne peux toujours pas expliquer votre dernier soupir.
cdmackay
@cdmackay Vous mélangez des backticks et des citations. En le citant, 'time' il perd son sens de mot réservé. Le faire en arrière en le faisant s'exécuter dans un sous-shell dont la sortie est épissée dans la commande. Cela n'a rien à voir avec la discussion. En effet, votre exemple `time\';dateprouve le contraire de votre affirmation: cela devrait donner une erreur par votre raisonnement car /usr/bin/timenécessite un argument. La raison pour laquelle ce n'est pas le cas, c'est que dans le sous-shell dans lequel il s'exécute, il est à nouveau le mot réservé time.
zwets
@arrange Les deux sont des erreurs de syntaxe et les deux sont signalés comme étant proches du même endroit, donc je n'y vois aucune incohérence. Une fois qu'il entre dans le pays des erreurs de syntaxe, vous ne pouvez pas vous attendre à ce qu'un analyseur sache comment s'en sortir. Si vous avez besoin de celle d'un analyseur, alors il doit connaître non seulement la syntaxe légale, mais aussi la syntaxe de chaque construction illégale possible, ce qui est impossible par définition.
zwets