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

33

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 montré qu’il cds’agit d’un shell intégré et d’ ifun mot clé. Alors, quelle est la différence entre shell builtin et mot clé?

Avinash Raj
la source
2
liées: unix.stackexchange.com/questions/267761/…
Ciro Santilli a commencé le

Réponses:

45

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 /binou /usr/binappelé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 timene s'agissait que d'une simple commande (même intégrée), il ne verrait que les arguments grep, ^#et l' /home/gniourf/.bashrcheure, puis sa sortie passerait par les autres parties du pipeline. Mais avec un mot clé, Bash peut tout gérer! il peut timele 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.

gniourf_gniourf
la source
2
D'accord avec @JohnyTex, c'est l'une des réponses les plus complètes et les plus appropriées que j'ai jamais vues sur les sites Stack. Merci. Une question peut - être sans rapport avec: juste pour l'amour de curiosité , je suis en train de trouver la documentation de la fonctionnalité « alias désactiver temporairement » à partir d' une commande précédente avec =\' à l'aide man, aproposet helpje ne l' ai pas eu de chance. Avez-vous une idée où je pourrais trouver cette information? Surtout pour pouvoir voir à l'avenir ce qu'il y a d'autre, car je pense qu'il me manque une source de référence.
nc
@nc: vous ne le trouverez pas explicitement documenté. La raison pour laquelle cela fonctionne est expliquée dans cette réponse. Le plus proche que vous trouverez est dans le manuel de référence dans la section Opération Shell . Vous constaterez que le développement des alias est effectué très tôt (une chose que j'ai tenté de souligner dans cette réponse), à ​​l'étape 2. La suppression des devis, le développement des paramètres, la suppression, etc. sont exécutés ultérieurement. Donc, pour désactiver un alias, vous pouvez utiliser une forme de citation pour empêcher le shell de comprendre un jeton comme un alias, par exemple \ll, "ll"ou 'll'.
gniourf_gniourf
1
En fait, j'ai reçu l'ordre, les alias et les mots clés se faisant virer arrivent en premier. Puisque nous citons le [[rendement, \[[il n'est pas analysé comme un alias. Droit jusqu'ici? Là où je me suis égaré, je ne réalisais pas que la barre oblique inverse était une citation de Bash, et j'ai essayé de la rechercher sous des pseudonymes et des mots-clés et je me suis complètement perdue ... Tout va bien maintenant. Sous la section Quotation:> Une barre oblique inverse () est le caractère d'échappement.
nc
1
Nous pouvons donc considérer (2) dans cette liste des opérations que Tokenization, et \[[est un simple jeton qui est du type LiteralQuoteToken, par opposition à [[ce qui sera un OpenTestKeywordToken, et qui nécessite un ]]CloseTestKeywordToken de fermeture pour une compilation / syntaxe correcte. Plus tard, dans (4), LiteralQuoteToken sera évalué en [[tant que nom de la commande bulitin / à exécuter, et dans (6), Bash sera verrouillé car il n'y a pas de [[commande intégrée. Agréable. J'oublierai peut-être les détails au fil du temps, mais pour le moment, la façon dont Bash gère les choses est beaucoup plus claire pour moi; Je vous remercie.
nc
9

man bashles appelle SHELL BUILTIN COMMANDS. Donc, un "shell intégré" est comme une commande normale, comme grep, etc., mais au lieu d’être contenu dans un fichier séparé, il 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 aucune fonction, mais nécessitent des commandes pour faire quoi que ce soit. ( À partir du lien, d' autres exemples sont for, while, doet !, et il y a plus dans ma réponse à votre autre question.)

Sparhawk
la source
1
Fait intéressant: [[ is a shell keywordmais [ is a shell builtin. Je ne sais pas pourquoi.
Sparhawk
1
Pour des raisons historiques et le standard POSIX, il est probable que le client se conforme le plus possible à l’ancien shell Bourne, car il [existait déjà sous forme de commande séparée dans la journée. [[n'est pas spécifié par la norme, les développeurs peuvent donc choisir de l'inclure en tant que mot clé ou en tant que fonction intégrée.
Sergiy Kolodyazhnyy
1

Le manuel de ligne de commande fourni avec Ubuntu ne donne pas de définition des mots-clés. Toutefois, le manuel en ligne (voir note suivante) et les spécifications standard du langage de commande POSIX Shell font référence à ceux-ci sous le nom de "mots réservés", qui fournissent une liste de ces mots. 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 d'une commande for (seuls dans et do 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 du 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 rien faire, et en fait , si vous entrez des mots clés tels que for, until, case- la coquille s'attendra une déclaration 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 , tandis que les répertoires intégrés ont un répertoire complet qui leur est dédié.

Sidenote

L'index GNU est affiché [comme mot réservé, mais il s'agit en fait d'une commande intégrée. [[en revanche est un mot réservé.

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

Sergiy Kolodyazhnyy
la source