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 fonction intégrée et qu'il [[
s'agit d'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 cela 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 [[
ce qui est un mot clé et comprend très tôt qu'il est spécial. Donc, il va chercher la fermeture ]]
et traitera l'intérieur d'une manière spéciale. 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, la suppression de la citation, l'expansion du nom de chemin et le fractionnement des mots ont lieu. La commande [
s'avère être construite dans le shell, elle l'exécute donc avec ces arguments, ce qui entraîne une erreur, d'où le problème.
En pratique, vous voyez que cette distinction autorise un comportement sophistiqué, ce qui ne serait pas possible avec les commandes intégrées.
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.-à-d. Aucun alias, aucune redirection, aucun mot-clé), de sorte qu'il ne fait que développer les variables. Après des extensions variables, il voit:
[ -d . ]
donc exécute la commande (intégrée) [
avec des arguments -d
, .
et ]
, ce qui, bien sûr, est vrai (cela ne fait que tester s'il .
s'agit d'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, étend 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 donc 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 aussi quelque chose d'assez spécial. 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é vs 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 se comportent vraiment comme des commandes: elles correspondent à une action exécutée avec des arguments soumis à une expansion directe des variables et à la division et à la suppression de mots. C’est vraiment comme avoir une commande externe quelque part /bin
ou /usr/bin
appelée avec les arguments donnés après le développement de variable, etc. Notez que quand je dis que c’est vraiment comme avoir une commande externe, je veux dire seulement en ce qui concerne les arguments, le fractionnement des mots, la suppression, expansion variable, etc. Une fonction intégrée peut modifier l'état interne du shell!
Les mots clés, en revanche, sont scannés et compris très tôt, et permettent un comportement sophistiqué du shell: le shell pourra interdire le fractionnement des mots ou l'expansion du chemin, 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
: c'est plus puissant d'avoir un mot-clé pour pouvoir 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
S'il time
ne s'agissait que d'une simple commande (même intégrée), il ne verrait que les arguments grep
, ^#
et l' /home/gniourf/.bashrc
heure, puis sa sortie passerait par les autres parties du pipeline. Mais avec un mot clé, Bash peut tout gérer! il peut time
le pipeline complet, y compris les redirections! Si nous time
étions 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?
En réalité, une commande intégrée est comme une commande, sauf qu'elle est intégrée au shell, alors qu'un mot clé est un comportement sophistiqué! on peut dire que cela fait partie de la grammaire de la coquille.