Por que o comando 'which' não funciona para 'cd'? Não consigo encontrar o executável para 'cd' também!

27

Eu tentei which cd e ele não deu um caminho, mas retornou o código de saída 1 (verificado com echo $? ). O coreutil cd está funcionando, então o executável deve estar lá, certo? Também executei um find para cd , mas não havia nenhum arquivo executável mostrado. Como é implementado então?

Atualização:

Eu não sei se devo perguntar isso em outro post, mas como acho que é bom aqui, estou expandindo (?) o post ... Então a resposta foi realmente muito simples, não há executável para isso - porque é um builtin - Mas eu encontrei alguns builtins (bash shell no Fedora) tem os arquivos executáveis! Então construído - & gt; nenhum executável não está certo, suponho? Talvez uma resposta explicando o que realmente são os builtins (comandos embutidos?), Que na verdade é o assunto aqui, em vez de focar mais em cd ... Alguns bons links postados anteriormente indicam que builtins não são programas ... então o que eles são ? Como eles funcionam? São apenas funções ou threads do shell?

    
por precise 06.10.2014 / 13:23

4 respostas

43

O comando cd não pode ser um executável

Em um shell, cd é usado para "entrar em outro diretório", ou mais formalmente, para alterar o diretório de trabalho atual (CWD). É impossível implementar isso como um comando externo:

O diretório pertence a um processo

O diretório de trabalho atual é o diretório usado para interpretar os caminhos relativos para obter um caminho completo que pode ser usado para acessar arquivos. Caminhos relativos são usados em muitos lugares, e a interpretação em um processo não deve influenciar outro processo.
Por esse motivo, todo processo tem seu próprio diretório de trabalho atual.

cd é sobre como alterar o diretório de trabalho atual do processo do shell, por exemplo, bash .

Se fosse um comando externo, um executável no caminho, executar esse executável criaria um processo com seu próprio diretório de trabalho, sem influenciar o do shell atual. Mesmo que o comando externo mude de diretório, essa alteração desaparece quando o processo externo sai.

Comandos internos do Shell

Portanto, não faz sentido executar um comando externo para a tarefa de cd . O comando cd precisa aplicar uma alteração no processo do shell em execução no momento.

Para fazer isso, é um "comando interno" do shell.

Os comandos internos são comandos que se comportam de maneira semelhante aos comandos externos, mas são implementados no shell (portanto, cd não faz parte dos coreutils). Isso permite que o comando mude o estado do próprio shell, nesse caso, para chamar chdir() see (veja man 2 chdir );

Sobre which

Agora, a resposta para a pergunta do título é fácil:
O comando executável which não pode nos dizer que cd é um comando interno porque um comando executável não sabe nada sobre builtins.

Alternativa type -a

Como uma alternativa para which , você pode usar type -a ; Pode ver comandos e builtins executáveis; Além disso, ele vê aliases e funções - também implementadas no 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
    
por Volker Siegel 06.10.2014 / 14:11
28

cd é um mandado pelo POSIX incorporado:

  

Se um comando simples resultar em um nome de comando e uma lista opcional de argumentos, as seguintes ações devem ser executadas:

     
  1. Se o nome do comando não contiver barras, a primeira etapa bem-sucedida na seguinte seqüência deverá ocorrer:
      ...      
    • Se o nome do comando corresponder ao nome de um utilitário listado na tabela a seguir, esse utilitário deverá ser chamado.
        ...
      cd
        ...
    •   
    • Caso contrário, o comando deve ser procurado usando o PATH ...
    •   
  2.   

Enquanto isso não diz explicitamente que tem que ser um built-in, a especificação continua, em a descrição de cd :

  

Como o cd afeta o ambiente atual de execução do shell, ele é sempre fornecido como um shell interno regular.

Do bash manual :

  

Os seguintes comandos internos do shell são herdados do Bourne Shell. Esses comandos são implementados conforme especificado pelo padrão POSIX.
  ...

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

Suponho que você poderia pensar em uma arquitetura em que cd não precise ser construído. No entanto, você tem que ver o que um built-in implica. Se você escrever um código especial no shell para fazer algo para algum comando, você está chegando perto de ter um builtin. Quanto mais você faz, melhor é simplesmente ter um builtin.

Por exemplo, você poderia ter o shell com IPC para se comunicar com subprocessos, e haveria um programa cd que verificaria a existência do diretório e se você tem permissão para acessá-lo e se comunica com o shell para dizer para mudar seu diretório. No entanto, você terá que verificar se o processo de comunicação com você é uma criança (ou fazer meios especiais de comunicação apenas com crianças, como um descritor de arquivo especial, memória compartilhada, etc.), e se o processo é de fato executando o programa cd confiável ou qualquer outra coisa. Isso é uma lata inteira de vermes.

Ou você pode ter um programa cd que torna o chdir system call, e inicia um novo shell com todas as variáveis de ambiente atuais aplicadas ao novo shell, e então mata seu shell pai (de alguma forma) quando feito. 1

Pior, você pode até ter um sistema onde um processo possa alterar os ambientes de outros processos (eu acho que tecnicamente você pode fazer isso com depuradores). No entanto, tal sistema seria muito, muito vulnerável.

Você se verá adicionando mais e mais códigos para proteger esses métodos, e é consideravelmente mais simples simplesmente incorporá-los.

Esse algo é um executável não impede que ele seja construído. Caso em questão:

echo e test

echo e test são utilitários obrigatórios do POSIX ( /bin/echo e /bin/test ). No entanto, quase todos os shell populares têm um echo e test incorporados. Da mesma forma, kill também é embutido que está disponível como um programa. Outros incluem:

  • sleep (não tão comum)
  • time
  • false
  • true
  • printf

No entanto, existem alguns casos em que um comando não pode ser nada além de um embutido. Um deles é cd . Normalmente, se o caminho completo não for especificado e o nome do comando corresponder ao de um arquivo interno, uma função adequada para esse comando será chamada. Dependendo do shell, o comportamento do builtin e do executável pode ser diferente (isso é particularmente um problema para echo , que tem comportamentos descontroladamente diferentes Se você quiser ter certeza do comportamento, é preferível chamar o executável usando o caminho completo e definir variáveis como POSIXLY_CORRECT (mesmo assim, não há garantia real).

Tecnicamente, nada impede que você forneça um sistema operacional que também seja um shell e tenha todos os comandos incorporados. Perto deste extremo está o BusyBox monolítico. BusyBox é um único binário, que (dependendo do nome com o qual é chamado) pode se comportar como qualquer um dos mais de 240 programas , incluindo um escudo Almquist ( ash ).Se você desabilitar PATH enquanto estiver executando o BusyBox ash , os programas disponíveis no BusyBox ainda estarão acessíveis para você sem especificar um PATH . Eles chegam perto de ser shell builtins, exceto que o shell em si é uma espécie de builtin para BusyBox.

Estudo de caso: O shell do Almquist do Debian ( dash )

Se você olhar a dash source, o encadeamento de execução é algo assim (claro, com funções adicionais envolvidas quando pipes e outras coisas são usadas):

main cmdloop evaltree evalcommand

evalcommand , em seguida, usa findcommand para determinar o que é o comando. Se é um builtin, então :

 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 é um struct ( struct builtincmd ), um de cujos membros é um ponteiro de função, com uma assinatura típica de main : (int, char **) . As chamadas de função evalbltin (dependendo se o embutido é o comando eval ou não) evalcmd ou esse ponteiro de função. As funções reais são definidas em vários arquivos de origem. echo , por exemplo, é :

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;
}

Todos os links para o código-fonte nesta seção são baseados em números de linha, então eles podem ser alterados sem aviso prévio.

Os sistemas POSIX

1 têm um cd executable .

Nota lateral:

Há muitos posts excelentes no Unix & amp; Linux que lida com o comportamento do shell. Em particular:

  

Se você ainda não notou um padrão nas questões listadas, quase todas envolvem Stéphane Chazelas .

    
por muru 06.10.2014 / 13:30
8

Você não pode encontrar um executável para cd porque não existe nenhum.

cd é um comando interno do seu shell (por exemplo, bash ).

    
por Uwe Plonus 06.10.2014 / 13:26
7

de man which :

  

que retorna os nomes de caminho dos arquivos (ou links) que seriam   executado no ambiente atual, teve seus argumentos   como comandos em um shell estritamente compatível com POSIX. Ele faz isso   procurando no PATH por arquivos executáveis que correspondam aos nomes dos   argumentos. Não segue links simbólicos.

Como podemos ver na descrição de which , está apenas verificando PATH . Então, se você implementou algum bash function , ele não mostrará nada. É melhor usar o comando type junto com which .

Por exemplo, no comando% ls do Ubuntu com alias para ls --color=auto .

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

$ which ls
/bin/ls

E se você implementar a função de teste hello :

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

which não mostra nada. Mas type :

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

No seu caso:

$ type cd
cd is a shell builtin

Isso significa que cd é um shell embutido , está dentro de bash . Todos bash builtins descritos em man bash , na seção 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.
    
por c0rp 06.10.2014 / 13:48