web-dev-qa-db-fra.com

Quelle est la différence entre Shell intégré et le mot clé Shell?

Quand je lance ces deux commandes, je reçois

$ type cd
cd is a Shell builtin
$ type if
if is a Shell keyword

Il est clairement indiqué que cd est un shell intégré et if est un mot clé Shell. Alors, quelle est la différence entre Shell intégré et mot clé?

32
Avinash Raj

Il existe une forte différence entre un mot clé intégré et un mot clé dans la façon dont Bash analyse votre code. Avant de parler de la différence, listons tous les mots-clés et éléments intégrés:

Builtins:

$ compgen -b
.         :         [         alias     bg        bind      break     
builtin   caller    cd        command   compgen   complete  compopt   
continue  declare   dirs      disown    echo      enable    eval      
exec      exit      export    false     fc        fg        getopts   
hash      help      history   jobs      kill      let       local     
logout    mapfile   popd      printf    pushd     pwd       read      
readarray readonly  return    set       shift     shopt     source    
suspend   test      times     trap      true      type      typeset   
ulimit    umask     unalias   unset     wait                          

Mots clés:

$ compgen -k
if        then      else      Elif      fi        case      
esac      for       select    while     until     do        
done      in        function  time      {         }         
!         [[        ]]        coproc              

Notez que, par exemple, [ est une commande intégrée et que [[ est un mot clé. Je vais utiliser ces deux exemples pour illustrer la différence ci-dessous, car ce sont des opérateurs bien connus: tout le monde les connaît et les utilise régulièrement (ou devrait).

Un mot-clé est analysé et compris par Bash très tôt dans son analyse. Cela permet par exemple de:

string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
    echo "The string is non-empty"
fi

Cela fonctionne bien, et Bash sera heureux

The string is non-empty

Notez que je n'ai pas cité $string_with_spaces. Considérant ce qui suit:

string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
    echo "The string is non-empty"
fi

montre que Bash n'est pas content:

bash: [: too many arguments

Pourquoi fonctionne-t-il avec des mots-clés et non avec des commandes intégrées? Parce que lorsque Bash analyse le code, il voit [[ qui est un mot clé et comprend très tôt qu'il est spécial. Donc, il cherchera le ]] de clôture et traitera l’intérieur de façon particulière. Une commande (ou commande) intégrée est traitée comme une commande réelle appelée avec des arguments. Dans ce dernier exemple, bash comprend qu'il doit exécuter la commande [ avec des arguments (affichés un par ligne):

-n
some
spaces
here
]

depuis l'expansion variable, suppression de devis, expansion du nom de chemin et fractionnement de Word se produit. La commande [ s’avérant être construite dans le shell, elle l’exécute donc avec ces arguments, ce qui entraîne une erreur, d’où la plainte.

En pratique, vous voyez que cette distinction autorise un comportement sophistiqué, ce qui ne serait pas possible avec les fonctions intégrées (ou les commandes).

Toujours dans la pratique, comment pouvez-vous distinguer un élément intégré d'un mot clé? C'est une expérience amusante à réaliser:

$ a='['
$ $a -d . ]
$ echo $?
0

Lorsque Bash analyse la ligne $a -d . ], il ne voit rien de spécial (c’est-à-dire, aucun alias, aucune redirection, aucun mot-clé), de sorte qu’il effectue simplement un développement variable. Après des extensions variables, il voit:

[ -d . ]

donc exécute la commande (intégrée) [ avec les arguments -d, . et ], ce qui est bien sûr vrai (ceci vérifie uniquement si . est un répertoire).

Maintenant regarde:

$ a='[['
$ $a -d . ]]
bash: [[: command not found

Oh. En effet, lorsque Bash voit cette ligne, il ne voit rien de spécial et, par conséquent, développe toutes les variables et finit par voir:

[[ -d . ]]

À l'heure actuelle, les extensions d'alias et l'analyse des mots clés ont été effectuées et ne le seront plus. Bash essaie de trouver la commande appelée [[, ne la trouve pas et se plaint.

Sûr les mêmes lignes:

$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found

et

$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found

L'expansion d'Alias ​​est quelque chose d'assez spécial aussi. Vous avez tous fait ce qui suit au moins une fois:

$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found

Le raisonnement est le même: le développement des alias se produit bien avant le développement des variables et la suppression des devis.


Mot clé v.s. Alias

Maintenant, que pensez-vous qu'il se passe si nous définissons un alias comme étant un mot clé?

$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0

Oh, ça marche! alors les alias peuvent être utilisés pour alias des mots-clés! Bon à savoir.


Conclusion: les commandes intégrées vraiment se comportent comme des commandes: elles correspondent à une action en cours d'exécution avec des arguments soumis à une expansion directe des variables et à la division et à la superposition de Word. C'est vraiment comme avoir une commande externe quelque part dans /bin ou /usr/bin qui est appelée avec les arguments donnés après le développement de variable, etc. Notez que quand je dis c'est vraiment comme avoir une commande externe je veux dire seulement avec en ce qui concerne les arguments, la division de Word, la suppression, le développement de variables, etc. Un processus intégré peut modifier l'état interne du shell!

Les mots clés, en revanche, sont analysés et compris très tôt, et permettent un comportement sophistiqué de Shell: le shell pourra interdire le fractionnement de mots ou l'expansion du chemin d'accès, etc.

Examinez maintenant la liste des mots-clés intégrés et des mots clés et essayez de comprendre pourquoi certains doivent être des mots-clés.


! est un mot clé. Il semble qu'il serait possible d'imiter son comportement avec une fonction:

not() {
    if "$@"; then
        return false
    else
        return true
    fi
}

mais cela interdirait des constructions comme:

$ ! ! true
$ echo $?
0

ou

$ ! { true; }
echo $?
1

Idem pour time: il est plus puissant d’avoir un mot clé qui lui permet de chronométrer les commandes composées complexes et les pipelines avec redirections:

$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null

Si time correspondait à une simple commande (même intégrée), il ne verrait que les arguments grep, ^# et /home/gniourf/.bashrc, le temps indiqué, puis sa sortie traverserait les parties restantes du pipeline. Mais avec un mot clé, Bash peut tout gérer! il peut time le pipeline complet, y compris les redirections! Si time était une simple commande, nous ne pourrions pas faire:

$ time { printf 'hello '; echo world; }

Essayez le:

$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'

Essayez de le réparer (?)

$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory

Désespéré.


Mot-clé vs alias?

$ alias mytime=time
$ alias myls=ls
$ mytime myls

Que penses-tu qu'il se passe?


Vraiment, un construit est semblable à une commande, sauf qu'il est construit dans le shell, alors qu'un mot clé est quelque chose qui permet un comportement sophistiqué! on peut dire que cela fait partie de la grammaire de Shell.

44
gniourf_gniourf

man bash les appelle Shell BUILTIN COMMANDS. Ainsi, un "shell builtin" ressemble à une commande normale, telle que grepname__, etc., mais au lieu d’être contenu dans un fichier séparé, c’est intégré à bash lui-même . Cela les rend plus efficaces que les commandes externes.

Un mot clé est également "codé en dur dans Bash, mais contrairement à une commande intégrée, un mot clé n'est pas en soi une commande, mais une sous-unité d'une construction de commande". J'interprète cela comme signifiant que les mots-clés n'ont pas de fonction à eux seuls, mais nécessitent des commandes pour faire quoi que ce soit. (À partir du lien, d'autres exemples sont forname__, whilename__, doet !, et il y en a d'autres dans ma réponse à votre autre question.)

9
Sparhawk

Le manuel en ligne de commande fourni avec Ubuntu ne donne pas de définition des mots-clés, cependant manuel en ligne (voir note) et spécifications standard POSIX Shell Command Language , reportez-vous à ceux-ci sont des "mots réservés", et les deux en fournissent des listes. Du standard POSIX:

Cette reconnaissance ne doit se produire que si aucun des caractères n’est cité et que le mot est utilisé comme:

  • Le premier mot d'une commande

  • Le premier mot suivant l’un des mots réservés autre que cas, pour ou dans

  • Le troisième mot d'une commande case (seul in est valide dans ce cas)

  • Le troisième mot dans une commande pour (seulement dans et ne sont valables dans ce cas)

La clé ici est que les mots clés/mots réservés ont une signification particulière car ils facilitent la syntaxe Shell, servent à signaler certains blocs de code tels que des boucles, des commandes composées, des instructions de branchement (si/cas), etc. Ils permettent de former des instructions de commande, mais par eux-mêmes - ne faites rien, et en fait, si vous entrez des mots-clés tels que for, until, case - le shell attend une instruction complète, sinon - erreur de syntaxe:

$ for
bash: syntax error near unexpected token `newline'
$  

Au niveau du code source, les mots réservés pour bash sont définis dans parese.y , alors que les fonctions intégrées ont répertoire entier qui leur est dédié.

Sidenote

L'indice GNU indique [ en tant que mot réservé, mais il s'agit en fait d'une commande intégrée. [[ est en revanche un mot réservé.

Voir aussi: Différences entre mot clé, mot réservé et propriété intégrée?

1
Sergiy Kolodyazhnyy