web-dev-qa-db-fra.com

Pourquoi la commande `which` ne fonctionne-t-elle pas pour` cd`? Je ne trouve pas non plus l'exécutable pour `cd`!

J'ai essayé which cd et il n'a pas donné de chemin mais a renvoyé le code de sortie 1 (vérifié avec echo $?). Le coreutil cd lui-même fonctionne, donc l'exécutable devrait être là, non? J'ai également exécuté un find pour cd, mais aucun fichier exécutable n'a été affiché. Comment est-il mis en œuvre alors?

Mettre à jour:

Je ne sais pas si je devrais poser cette question dans un autre post, mais comme je pense que c'est bien ici, j'élargis (?) Le post ... Donc, la réponse était assez simple, il n'y a pas d'exécutable pour cela - car c'est a intégré - Mais j'ai trouvé des éléments intégrés (bash Shell dans Fedora) avoir les fichiers exécutables! Donc, intégré -> aucun exécutable n'est pas correct, je suppose? Peut-être une réponse expliquant ce que sont les commandes intégrées (commandes intégrées?), Ce qui est le problème ici, plutôt que de se concentrer davantage sur cd... Quelques bons liens postés précédemment indiquent que les instructions intégrées ne sont pas des programmes ... alors que sont-elles? Comment travaillent-ils? S'agit-il uniquement de fonctions ou de threads du shell?

30
precise

La commande cd ne peut pas être un exécutable

Dans un shell, cd est utilisé pour "aller dans un autre répertoire", ou plus formellement, pour changer le répertoire de travail actuel (CWD). Il est impossible d'implémenter cela en tant que commande externe:

Le répertoire appartient à un processus

Le répertoire de travail actuel est le répertoire utilisé pour interpréter les chemins relatifs afin d'obtenir un chemin complet pouvant être utilisé pour accéder aux fichiers. Les chemins relatifs sont utilisés dans de nombreux endroits et l'interprétation d'un processus ne doit pas influencer un autre processus.
Pour cette raison, chaque processus a son propre répertoire de travail actuel.

cd concerne la modification du répertoire de travail actuel du processus Shell, par exemple bash.

S'il s'agissait d'une commande externe, un exécutable dans le chemin, son exécution créerait un processus avec son propre répertoire de travail, sans influencer celui du Shell en cours. Même si la commande externe change de répertoire, cette modification disparaît à la fin du processus externe.

Commandes intégrées Shell

Il n’a donc aucun sens de lancer une commande externe pour la tâche cd. La commande cd doit appliquer une modification au processus Shell en cours d'exécution.

Pour ce faire, il s’agit d’une "commande intégrée" du shell.

Les commandes intégrées sont des commandes qui se comportent de manière similaire aux commandes externes, mais sont implémentées dans le shell (donc, cd ne fait pas partie des coreutils). Cela permet à la commande de modifier l'état du shell lui-même, dans ce cas, d'appeler chdir() see (voir man 2 chdir );

À propos de which

Maintenant, la réponse à la question du titre est simple:
La commande exécutable which ne peut pas nous dire que cd est une commande intégrée car une commande exécutable ne sait rien des opérations intégrées.

Alternative type -a

Au lieu de which, vous pouvez utiliser type -a; Il peut voir les commandes exécutables et les commandes intégrées; De plus, il voit des alias et des fonctions - également implémentés dans le shell:

$ type -a cd
cd is a Shell builtin
$ type -a type
type is a Shell builtin
$ type -a which
which is /usr/bin/which
which is /bin/which
46
Volker Siegel

cd est un POSIX-mandated Intégré dans le shell:

Si une commande simple donne un nom de commande et une liste facultative d'arguments, les actions suivantes doivent être effectuées:

  1. Si le nom de la commande ne contient pas de barre oblique, la première étape réussie de la séquence suivante doit avoir lieu:
    ...
    • Si le nom de la commande correspond au nom d'un utilitaire répertorié dans le tableau suivant, cet utilitaire doit être appelé.
      ...
      cd
      ...
    • Sinon, la commande doit être recherchée à l'aide de PATH ...

Bien que cela ne dise pas explicitement que cela doit être intégré, la spécification ajoute: la description de cd :

Etant donné que cd affecte l’environnement d’exécution du shell actuel, il est toujours fourni en tant qu’intégré du shell.

Depuis le bash manual :

Les commandes intégrées Shell suivantes sont héritées du Bourne Shell. Ces commandes sont implémentées comme spécifié par le standard POSIX.
...

cd
       cd [-L|[-P [-e]]] [directory]

Je suppose que vous pourriez penser à une architecture où cd ne doit pas nécessairement être intégré. Cependant, vous devez voir ce qu’implique une fonction intégrée. Si vous écrivez un code spécial dans le shell pour faire quelque chose pour une commande quelconque, vous êtes sur le point d'avoir une commande intégrée. Plus vous en faites, mieux c'est d'avoir simplement une fonction intégrée.

Par exemple, vous pouvez demander au shell d’avoir IPC pour communiquer avec les sous-processus, et il y aurait un programme cd qui vérifierait l’existence du répertoire et si vous disposiez de l’autorisation d’y accéder, puis de communiquer avec le Shell pour lui dire de changer de répertoire. Cependant, vous devrez ensuite vérifier si le processus qui communique avec vous est un enfant (ou créer un moyen de communication spécial uniquement avec les enfants, tel qu'un descripteur de fichier spécial, la mémoire partagée, etc.), et si le processus est réellement exécuter le programme cd de confiance ou autre chose. C'est une boîte de Pandore.

Ou vous pourriez avoir un programme cd qui effectue l'appel système chdir , et démarre un nouveau shell avec toutes les variables d'environnement actuelles appliquées au nouveau shell, puis élimine son shell parent (en quelque sorte) lorsque vous avez terminé. 1

Pire, vous pourriez même avoir un système où un processus peut modifier les environnements d'autres processus (techniquement, je pense que vous pouvez le faire avec des débogueurs). Cependant, un tel système serait très très vulnérable.

Vous serez amené à ajouter de plus en plus de code pour sécuriser de telles méthodes, et il est beaucoup plus simple de simplement en faire un outil intégré.


Que quelque chose soit un exécutable n'empêche pas qu'il soit intégré. Exemple:

echo et test

echo et test sont des utilitaires commandés par POSIX (/bin/echo et /bin/test). Pourtant, presque tous les Shell populaires ont un echo et un test intégrés. De même, kill est également intégré et disponible en tant que programme. Parmi les autres:

  • sleep (pas aussi commun)
  • time
  • false
  • true
  • printf

Cependant, dans certains cas, une commande ne peut être que intégrée. L'un d'entre eux est cd. Généralement, si le chemin d'accès complet n'est pas spécifié et que le nom de la commande correspond à celui d'une commande intégrée, une fonction adaptée à cette commande est appelée. Selon le shell, le comportement de l’intégré et celui de l’exécutable peuvent différer (il s’agit en particulier de un problème pour echo , qui a des comportements extrêmement différents . Si vous voulez être certain du comportement, il est préférable d'appeler l'exécutable en utilisant le chemin complet et de définir des variables telles que POSIXLY_CORRECT (même dans ce cas, il n'y a pas de garantie réelle).

Techniquement, rien ne vous empêche de fournir un système d’exploitation qui est aussi un shell et qui possède toutes les commandes intégrées. Près de cette extrémité extrême est le monolithique BusyBox . BusyBox est un simple binaire qui (en fonction du nom avec lequel il est appelé) peut se comporter comme n'importe quel sur 240 programmes , y compris un shell Almquist (ash). Si vous désactivez PATH lors de l'exécution de BusyBox ash, les programmes disponibles dans BusyBox sont toujours accessibles sans spécifier PATH. Ils s'approchent de ceux de Shell, à la différence que Shell lui-même est en quelque sorte intégré à BusyBox.


Étude de cas: Le shell Debian Almquist (dash)

Si vous regardez la source dash, le thread d'exécution ressemble à ceci (bien sûr, avec des fonctions supplémentaires impliquées lorsque des tubes ou d'autres choses sont utilisées):

maincmdloopevaltreeevalcommand

evalcommand utilise ensuite findcommand pour déterminer la nature de la commande. S'il s'agit d'une commande intégrée, then :

 case CMDBUILTIN:
     if (spclbltin > 0 || argc == 0) {
         poplocalvars(1);
         if (execcmd && argc > 1)
             listsetvar(varlist.list, VEXPORT);
     }
     if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
         if (exception == EXERROR && spclbltin <= 0) {
             FORCEINTON;
             break;

cmdentry.u.cmd est un struct ( struct builtincmd ), dont l'un des membres est un pointeur de fonction, avec une signature typique de main: (int, char **). Les appels de fonction evalbltin (selon que la commande intégrée est la commande eval ou non) soit evalcmd, ou le pointeur de cette fonction. Les fonctions réelles sont définies dans divers fichiers source. echo, par exemple, est :

int
echocmd(int argc, char **argv)
{
    int nonl;

    nonl = *++argv ? equal(*argv, "-n") : 0;
    argv += nonl;

    do {
        int c;

        if (likely(*argv))
            nonl += print_escape_str("%s", NULL, NULL, *argv++);
        if (nonl > 0)
            break;

        c = *argv ? ' ' : '\n';
        out1c(c);
    } while (*argv);
    return 0;
}

Tous les liens vers le code source dans cette section sont basés sur le numéro de ligne, ils peuvent donc changer sans préavis.


1 Les systèmes POSIX ont un cd exécutable .


Note de côté:

Il y a beaucoup d'excellents articles sur Unix et Linux qui traitent du comportement de Shell. En particulier:

Si vous n'avez pas remarqué de tendance dans les questions répertoriées jusqu'à présent, elles impliquent presque toutes Stéphane Chazelas .

29
muru

Vous ne pouvez pas trouver d’exécutable pour cd car il n’en existe aucun.

cd est une commande interne de votre shell (par exemple, bash).

8
Uwe Plonus

de man which:

qui renvoie les noms de chemin des fichiers (ou liens) qui seraient exécutés dans l'environnement actuel si ses arguments avaient été fournis sous forme de commandes dans un shell strictement conforme à POSIX. Pour ce faire, il recherche dans le PATH les fichiers exécutables correspondant aux noms des arguments. Il ne suit pas de liens symboliques.

Comme nous pouvons le voir à la description de whichname__, il ne fait que vérifier PATHname__. Donc, si vous avez implémenté bash function, il ne vous montrera rien. Il est préférable d'utiliser la commande typeavec whichname __.

Par exemple, dans Ubuntu, lsname__, commande alias ls --color=auto.

$ type ls
ls is aliased to `ls --color=auto'

$ which ls
/bin/ls

Et si vous implémentez la fonction de test helloname__:

$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello

whichne montre rien. Mais typename__:

$ type hello
hello is a function
hello () 
{ 
    for i in {1,2,3};
    do
        echo Hello $i;
    done
}

Dans votre cas:

$ type cd
cd is a Shell builtin

Cela signifie que cdest un construit dans le shell , il est dans bashname__. Toutes les commandes intégrées à bash décrites dans man bash, section Shell BUILTIN COMMANDS

Shell BUILTIN COMMANDS
       Unless otherwise noted, each builtin command documented in this section
       as accepting options preceded by - accepts -- to signify the end of the
       options.   The  :, true, false, and test builtins do not accept options
       and do not treat -- specially.  The exit, logout, break, continue, let,
       and  shift builtins accept and process arguments beginning with - with‐
       out requiring --.  Other builtins that accept  arguments  but  are  not
       specified  as accepting options interpret arguments beginning with - as
       invalid options and require -- to prevent this interpretation.
7
c0rp