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

30

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

Mise à jour:

Je ne sais pas si je devrais poser cette question dans un autre article mais comme je pense que c'est bien ici, j'étends (?) L'article ... Donc la réponse était en fait assez simple, il n'y a pas d'exécutable pour ça - parce que c'est un module intégré - Mais j'ai trouvé que certains modules intégrés (shell bash dans Fedora) ont les fichiers exécutables! Donc intégré -> aucun exécutable n'est pas vrai, je suppose? Peut-être une réponse expliquant ce que sont les commandes intégrées (commandes intégrées?), Ce qui est en fait le problème ici, plutôt que de se concentrer davantage sur cd... Certains bons liens publiés précédemment indiquent que les commandes intégrées ne sont pas des programmes ... alors qu'est-ce que c'est? Comment travaillent-ils? S'agit-il simplement de fonctions ou de threads du shell?

précis
la source
1
Lisez cette réponse. Il est suggéré d'utiliser la typecommande
c0rp
7
Voir cette Q&R sur pourquoi cddoit être un programme intégré: pourquoi le cd n'est-il pas un programme? et celui-ci sur pourquoi typevaut mieux que which: Pourquoi ne pas utiliser "qui"? Que faut-il utiliser alors?
terdon
Question similaire ici: askubuntu.com/q/613470/178596
Wilf

Réponses:

46

La commande cdne peut pas être un exécutable

Dans un shell, cdest utilisé pour "aller dans un autre répertoire", ou plus formellement, pour changer le répertoire de travail courant (CWD). Il est impossible de l'implémenter 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 à de nombreux endroits et l'interprétation dans un processus ne doit pas influencer un autre processus.
Pour cette raison, chaque processus a son propre répertoire de travail actuel.

cdconsiste à changer le 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, exécutant 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 change son répertoire, ce changement disparaît lorsque le processus externe se termine.

Commandes intégrées au shell

Il est donc inutile d'exécuter une commande externe pour la tâche de cd. La commande cddoit 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 (ne font donc cdpas 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);

Sur which

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

Alternative type -a

Comme alternative à which, vous pouvez utiliser type -a; Il peut voir les commandes et les commandes exécutables; 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
Volker Siegel
la source
1
Grande explication!
SaltyNuts
3
Beaucoup mieux que la réponse actuellement acceptée - cela explique pourquoi cd un shell est intégré.
Lily Chung
28

cdest un shell shell mandaté par POSIX:

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 aucune barre oblique, la première étape réussie de 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 à l'aide du CHEMIN ...

Bien que cela ne dise pas explicitement qu'il doit être intégré, la spécification continue dans la description decd :

Puisque cd affecte l'environnement d'exécution du shell actuel, il est toujours fourni en tant que shell standard intégré.

Du bashmanuel :

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

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

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

Par exemple, vous pouvez demander au shell d'avoir IPC pour communiquer avec les sous-processus, et il y aurait un cdprogramme qui vérifierait l'existence du répertoire et si vous avez l'autorisation d'y accéder, puis communique avec le shell pour lui dire de changer son annuaire. Cependant, vous devrez ensuite 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 cdprogramme de confiance ou autre chose. C'est toute une boîte de vers.

Ou vous pourriez avoir un cdprogramme qui fait l' chdirappel système, et le démarre un nouveau shell avec toutes les variables d'environnement actuelles appliquées au nouveau shell, puis tue son shell parent (en quelque sorte) 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 des 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 ces méthodes, et il est considérablement plus simple d'en faire simplement un intégré.


Que quelque chose soit un exécutable ne l'empêche pas d'être intégré. Exemple:

echo et test

echoet testsont des utilitaires mandatés par POSIX ( /bin/echoet /bin/test). Pourtant, presque tous les shell populaires ont un intégré echoet test. De même, killest également intégré qui est disponible en tant que programme. Parmi les autres:

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

Cependant, il existe certains cas où une commande ne peut pas être autre chose qu'une fonction intégrée. L'un d'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'un code intégré, une fonction adaptée à cette commande est appelée. Selon le shell, le comportement du builtin et celui de l'exécutable peuvent différer (c'est particulièrement un problème pourecho , 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 complet et définissez des variables comme POSIXLY_CORRECT(même alors, il n'y a pas de réelle garantie).

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


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

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

maincmdloopevaltreeevalcommand

evalcommandutilise ensuite findcommandpour déterminer la commande. S'il s'agit d'une fonction intégrée, 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.cmdest un struct( struct builtincmd), dont un des membres est un pointeur de fonction avec une signature typique de main: (int, char **). La evalbltinfonction appelle (selon que la evalcommande intégrée est la commande ou non) evalcmdou ce pointeur de fonction. Les fonctions réelles sont définies dans divers fichiers source. echo, par exemple, c'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 un numéro de ligne, ils peuvent donc changer sans préavis.


1 Les systèmes POSIX ont un cdexécutable .


Note latérale:

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

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

muru
la source
4
Notez que vous pouvez obtenir le cdtexte d'aide avec help cd(la même chose pour toutes les commandes intégrées au shell)
Sylvain Pineau
@SylvainPineau même si je suis lié au manuel bash, ce conseil n'est généralement pas applicable à d'autres shells, comme zsh.
muru
En effet, helpc'est un bash intégré (pour zsh, c'est run-help cd)
Sylvain Pineau
La description liée de la spécification POSIX ne dit pas explicitement que cela cddoit être comme un shell intégré ... mais basé sur la façon dont les propriétés de processus et leur transfert fonctionnent sous UNIX cdcomme un shell intégré est la seule implémentation simple. Voir la réponse de Volker Siegel .
pabouk
@pabouk en effet (il l'appelle un utilitaire), puis continue: "Puisque cd affecte l'environnement d'exécution du shell actuel, il est toujours fourni en tant que shell standard intégré."
muru
8

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

cdest une commande interne de votre shell (par exemple bash).

Uwe Plonus
la source
7

de man which:

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

Comme nous pouvons le voir dans la description de which, il s'agit uniquement de vérifier PATH. Donc, si vous en avez implémenté bash function, cela ne vous montrera rien. Il est préférable d'utiliser la typecommande avec which.

Par exemple, dans la lscommande Ubuntu alias pour 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

whichne montre rien. Mais type:

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

Dans ton cas:

$ type cd
cd is a shell builtin

Cela signifie que cdc'est un shell intégré , il est à l'intérieur bash. Toutes les commandes bash décrites dans man bash, dans la section COMMANDES DE CONSTRUCTION DE SHELL

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.
c0rp
la source
2
Mmmm,manwhich .
IQAndreas
1
Il convient peut-être de souligner davantage: ne pas utiliser which, utiliser type.
tripleee