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é?
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.
man bash
les appelle Shell BUILTIN COMMANDS
. Ainsi, un "shell builtin" ressemble à une commande normale, telle que grep
name__, 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 for
name__, while
name__, do
et !
, et il y en a d'autres dans ma réponse à votre autre question.)
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é.
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?