Pourquoi la commande «which» ne fonctionnerait-elle pas pour «cd»? Je ne trouve pas non plus l'exécutable pour 'cd'!

28

J'ai essayé which cd et celui-ci ne donnait pas de chemin mais renvoyait 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'était affiché. Comment est-il mis en œuvre alors?

Mise à 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 en fait assez simple, il n'y a pas d'exécutable pour ça - parce que c'est un intégré - Mais j'ai trouvé des fichiers intégrés (bash shell dans Fedora) ont les fichiers exécutables! Donc intégré - & gt; aucun exécutable n'est correct, je suppose? Peut-être une réponse expliquant ce que sont réellement les commandes intégrées (commandes intégrées?), Ce qui est en fait le cas ici, plutôt que de se concentrer davantage sur cd ... ? Comment travaillent-ils? Sont-ils juste des fonctions ou des threads du shell?

    
posée precise 06.10.2014 - 13:23
la source

4 réponses

45

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

Dans un shell, cd est utilisé pour "aller dans un autre répertoire" ou, de manière plus formelle, pour modifier le répertoire de travail en cours (CWD). Il est impossible de l’implémenter en tant que commande externe:

Le répertoire appartient à un processus

Le répertoire de travail en cours 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 dans un processus ne devrait pas influencer un autre processus.
Pour cette raison, chaque processus possède son propre répertoire de travail.

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

Si c'était une commande externe, un exécutable dans le chemin, exécuter cet exécutable créerait un processus avec son propre répertoire de travail, sans influencer celui du shell actuel. Même si la commande externe modifiait son répertoire, cette modification disparaît lorsque le processus externe est terminé.

Commandes intégrées au shell

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

Pour ce faire, c'est une "commande intégrée" du shell.

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

A propos de which

Maintenant, la réponse à la question du titre est facile:
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 à propos des commandes intégrées.

Alternative type -a

Comme alternative à 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
    
réponse donnée Volker Siegel 06.10.2014 - 14:11
la source
29

cd est un shell intégré au shell POSIX :

  

Si une commande simple génère un nom de commande et une liste d’arguments facultative, les actions suivantes doivent être effectuées:

     
  1. Si le nom de la commande ne contient aucune barre oblique, la première étape réussie dans la séquence suivante doit se produire:
      ...      
    • 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 en utilisant le PATH ...
    •   
  2.   

Bien que cela ne dise pas explicitement qu’il doit être intégré, la spécification continue en disant: la description de cd :

  

Comme cd affecte l’environnement d’exécution du shell en cours, il est toujours fourni comme un shell intégré au shell.

Du manuel bash :

  

Les commandes intégrées suivantes du shell sont héritées du shell Bourne. 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 une version intégrée. Cependant, vous devez voir ce que implique un intégré. Si vous écrivez un code spécial dans le shell pour faire quelque chose pour une commande, vous êtes sur le point d’avoir une commande intégrée. Plus vous en faites, plus il est simple d'avoir une version intégrée.

Par exemple, vous pouvez faire en sorte que le shell ait IPC pour communiquer avec des sous-processus, et il y aura un programme cd qui vérifiera l'existence du répertoire et si vous êtes autorisé à accéder et à communiquer avec le shell. lui dire de changer son répertoire. Cependant, vous devrez vérifier si le processus de communication avec vous est un enfant (ou créer des moyens de communication spéciaux avec les enfants uniquement, tels qu'un descripteur de fichier spécial, une mémoire partagée, etc.) et si le processus est en fait exécuter le programme cd de confiance ou autre chose. C'est une boîte entière de vers.

Ou vous pourriez avoir un programme cd qui rend le chdir appel système, et lance un nouveau shell avec toutes les variables d'environnement actuelles appliquées au nouveau shell, puis tue son shell parent (d'une manière ou d'une autre) une fois terminé. 1

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

Vous vous retrouverez à ajouter de plus en plus de code pour sécuriser de telles méthodes, et il est beaucoup plus simple de simplement en faire une méthode intégrée.

Que quelque chose est un exécutable ne l’empêche pas d’être une version intégrée. Par exemple:

echo et test

echo et test sont des utilitaires POSIX ( /bin/echo et /bin/test ). Pourtant, presque tous les shell populaires ont un echo et test intégré. De même, kill est également intégré à un programme. D'autres incluent:

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

Cependant, il existe des cas où une commande ne peut pas être autre chose qu'une commande intégrée. L'un d'entre eux est cd . En règle générale, 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 du fichier intégré et celui de l'exécutable peuvent différer (ceci est en particulier un problème pour echo , qui a des comportements très différents . Si vous voulez être certain du comportement, il est préférable d'appeler l'exécutable en utilisant le chemin d'accès complet et de définir des variables comme POSIXLY_CORRECT (même dans ce cas, il n'y a aucune garantie réelle).

Techniquement, rien ne vous empêche de fournir un système d'exploitation qui est également un shell et qui possède toutes les commandes en tant que paramètre intégré. Près de cette extrémité extrême se trouve la monolithique BusyBox . BusyBox est un binaire unique, qui (selon le nom avec lequel il est appelé) peut se comporter comme l'un des plus de 240 programmes , y compris une coque Almquist ( ash ).Si vous désactivez PATH lors de l'exécution de BusyBox ash , les programmes disponibles dans BusyBox vous sont toujours accessibles sans spécifier de PATH . Ils sont proches de l’installation de shell, sauf que le shell lui-même est une sorte de bus intégré à BusyBox.

Etude de cas: Le shell Debian Almquist ( dash )

Si vous regardez la source dash , le thread d'exécution est quelque chose comme ça (bien sûr, avec des fonctions supplémentaires impliquées lorsque des pipes et d'autres choses sont utilisées):

main cmdloop evaltree evalcommand

evalcommand utilise ensuite findcommand pour déterminer quelle est la commande. S'il s'agit d'un fichier intégré, alors :

 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 à la fonction evalbltin (selon que la commande intégrée est la commande eval ) ou non) soit evalcmd , soit ce pointeur de fonction. Les fonctions réelles sont définies dans divers fichiers sources. Par exemple, echo 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 des numéros de ligne, ils peuvent donc être modifiés sans préavis.

Les systèmes

1 POSIX ont un cd executable .

Côté note:

Il y a beaucoup d'excellents articles sur Unix & amp; Linux qui traite du comportement du shell. En particulier:

  

Si vous n’avez pas remarqué de modèle dans les questions listées jusqu’à présent, presque toutes impliquent Stéphane Chazelas .

    
réponse donnée muru 06.10.2014 - 13:30
la source
8

Vous ne pouvez pas trouver d'exécutable pour cd car il n'y en a pas.

cd est une commande interne de votre shell (p. ex. bash ).

    
réponse donnée Uwe Plonus 06.10.2014 - 13:26
la source
7

de man which :

  

qui renvoie les noms de chemin des fichiers (ou des liens) qui seraient   exécuté dans l'environnement actuel, si ses arguments avaient été donnés   en tant que commandes dans un shell strictement conforme à POSIX. Il le fait par   rechercher le PATH pour les fichiers exécutables correspondant aux noms des   arguments. Il ne suit pas les liens symboliques.

Comme nous pouvons le voir dans la description de which , il ne vérifie que PATH . Donc, si vous avez implémenté du bash function , cela ne vous montrera rien. Il est préférable d’utiliser la commande type avec which .

Par exemple, dans la commande Ubuntu ls 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 hello :

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

which ne montre rien. Mais type :

$ 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 cd est un shell intégré , il se trouve à l'intérieur de bash . Toutes les bases de Bash décrites dans man bash , dans la 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.
    
réponse donnée c0rp 06.10.2014 - 13:48
la source

Lire d'autres questions sur les étiquettes