Comment bash fait-il la différence entre le développement de croisillons et le regroupement de commandes?

48

J'ai remarqué que l' {on peut utiliser pour l'expansion du corset:

echo {1..8}

ou en groupe de commandes:

{ls;echo hi}

Comment bash sait-il la différence?

source d'amour
la source
1
Excellente question, +1. Il semble que cela puisse être {interprété comme une liste de commandes s'il apparaît au début d'une commande et comme un développement d'accolade sinon, mais je ne suis pas sûr.
Celada
16
{ls;echo hi}n'est pas légal bash. Vous avez besoin d'un espace après l'accolade d'ouverture et d'un point-virgule avant celui de clôture.
PSkocik

Réponses:

39

Une raison simplifiée est l'existence d'un caractère: space.

Les développements d'accolades ne traitent pas les espaces (non cités).

Une {...}liste a besoin d'espaces (non cités).

La réponse plus détaillée est la façon dont le shell analyse une ligne de commande .


La première étape pour analyser (comprendre) une ligne de commande consiste à la diviser en plusieurs parties.
Ces parties (généralement appelées mots ou jetons) résultent de la division d'une ligne de commande à chaque méta-caractère du lien :

  1. Divise la commande en jetons séparés par le jeu fixe de méta-caractères: ESPACE, TAB, NEWLINE,;, (,), <,>, | et &. Les types de jetons incluent des mots, des mots-clés, des redirecteurs d'E / S et des points-virgules.

Méta-personnages: spacetabenter;,<>|et &.

Après division, les mots peuvent être d'un type (tel que compris par la coque):

  • Commandes préalables: LC=ALL ...
  • Commander LC=ALL echo
  • Arguments LC=ALL echo "hello"
  • La redirection LC=ALL echo "hello" >&2

Expansion de Brace

Si une "chaîne d'accolade" (sans espaces ni méta-caractères) est un mot unique (comme décrit ci-dessus) et qu'elle n'est pas entre guillemets , elle est candidate pour le "développement d' accolade ". Plus de contrôles sont effectués sur la structure interne plus tard.

Ainsi, ceci: est {ls,-l}qualifié d '"extension de support" pour devenir ls -l, en tant que first wordou argument(dans bash, zsh est différent).

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

Mais ce ne sera pas: {ls ,-l}. Bash se divisera spaceet analysera la ligne en deux mots: {lset ,-l}qui déclenchera un command not found(l'argument ,-l}est perdu):

 $ {ls ,-l}
 bash: {ls: command not found

Votre ligne: {ls;echo hi}ne deviendra pas une "extension Brace" à cause des deux méta-caractères ;et space.

Il sera divisé en ces trois parties: {lsnouvelle commande: echo hi}. Comprenez que cela ;déclenche le démarrage d'une nouvelle commande. La commande {lsne sera pas trouvée et la prochaine commande sera imprimée hi}:

$ {ls;echo hi}
bash: {ls: command not found
hi}

S'il est placé après une autre commande, il lancera quand même une nouvelle commande après le ;:

$ echo {ls;echo hi}
{ls
hi}

liste

L' une des « commandes composé » est une « liste Brace » (mes mots): { list; }.
Comme vous pouvez le constater, il est défini avec des espaces et une fermeture ;.
Les espaces et ;sont nécessaires parce que les deux {et }sont « réservées mots ».

Et par conséquent, pour être reconnu comme un mot, il doit être entouré de méta-caractères (presque toujours:) space.

Comme décrit au point 2 de la page liée

  1. Vérifie le premier jeton de chaque commande pour voir s'il s'agit de ...., {, ou (, alors la commande est en fait une commande composée.

Votre exemple: {ls;echo hi}n'est pas une liste.

Il faut une fermeture ;et un espace (au moins) après {. Le dernier }est défini par la fermeture ;.

Ceci est une liste { ls;echo hi; }. Et ceci { ls;echo hi;}est également (moins couramment utilisé, mais valide) (Merci @choroba pour l'aide).

$ { ls;echo hi; }
A-list-of-files
hi

Mais comme argument (le shell connaît la différence) à une commande, il déclenche une erreur:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

Mais faites attention à ce que vous pensez que la coque analyse:

$ echo { ls;echo hi;
{ ls
hi

la source
2
c'est vraiment la meilleure réponse, car vous nous expliquez comment fonctionne l'analyseur bash! et avec une explication de détail!
Lovespring
2
Vous n'avez pas besoin d'espace entre ;et }. { ls;}fonctionne comme le point-virgule est déjà un méta-caractère.
Choroba
1
@ Lovespring Merci, oui j'ai investi du temps dans l'écriture. Je suis heureux de savoir que c'est utile. Merci encore.
excellent article, merci beaucoup pour les références
Edward Torvalds le
16

Le bloc {est un mot-clé shell, il doit donc être séparé du mot suivant par un espace. En mode d'accolade, il ne doit pas y avoir d'espace (si vous devez accoler pour agrandir un espace, vous devez y échapper:) echo {\ ,a}{b,c}.

Vous pouvez utiliser le développement d'accolade au début d'une commande:

{ls,.}  # expands to "ls ."

Cependant, vous ne pouvez pas l'utiliser pour développer un bloc, car l'analyse des commandes de regroupement a lieu avant les extensions:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory
choroba
la source
5

Il sait en vérifiant la syntaxe de la ligne de commande. De la même manière, il sait que dans l'expression echo echo, le premier écho doit être traité comme une commande et le deuxième comme un paramètre du premier écho.

En bash c'est très simple, puisque { cmd; }devrait avoir des espaces et des points-virgules. Cependant, par exemple en zsh, ils ne sont pas nécessaires, mais néanmoins, en analysant le contexte du {}shell, on peut dire ce qu'il faut faire avec son contenu.

Considérer ce qui suit:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

Les deux renvoient la date actuelle, mais

echo {1..3}

retourne 1 2 3parce que le shell connaît {}un argument de commande echo, il devrait donc être développé.

Jimmij
la source
{suivi d'un espace non cité ne commence pas l'expansion de l'accolade dans bash.
Choroba
@choroba Oui, et pas seulement juste après {. L'espace non entre guillemets ne peut être n'importe où car le shell divise la ligne de commande entière en espaces
Jimmij
0

Tout d’abord, l’accolade composée doit être un mot et le premier mot de la ligne de commande:

echo { these braces are just words }

Deuxièmement, les accolades individuelles ne sont pas spéciales (comme vous pouvez le voir ci-dessus). Les accolades vides ne sont pas spéciales:

echo {} # just the token {}: familiar from the find command

Tout ce qui est sans virgule est aussi juste

echo {abc} # just {abc}

Voici où l'action commence.

echo {a,b} # braces disappear, a b results.

Donc, fondamentalement, pour que l’attelle se développe, nous avons besoin d’un seul mot (non séparé en champs sur des espaces), dans lequel se trouve au moins une instance {...}dans laquelle se trouve au moins une virgule.

Cela peut être le premier mot de la ligne de commande, au fait:

{ls,-l} .   # just "ls -l ."
Kaz
la source