Por que o comando `which` não funcionaria para o` cd`? Também não consigo encontrar o executável para `cd`!

30

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

Atualizar:

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 bastante simples, não há executável para isso - porque é um builtin - Mas eu descobri que alguns builtins (bash shell no Fedora) têm os arquivos executáveis! Então embutido -> nenhum executável não está certo, suponho? Talvez uma resposta que explique o que são realmente os builtins (comandos internos?), O que realmente é o problema aqui, em vez de se concentrar mais em cd... Alguns bons links publicados anteriormente indicam que os builtins não são programas ... então, o que são? Como eles funcionam? Eles são apenas funções ou threads do shell?

preciso
fonte
1
Leia esta resposta. Sugere-se usar o typecomando
c0rp
7
Veja estas perguntas e respostas sobre por que cdprecisa ser incorporado: Por que o CD não é um programa? e este sobre por que typeé melhor que which: Por que não usar "what"? O que usar então?
terdon
Pergunta semelhante aqui: askubuntu.com/q/613470/178596
Wilf

Respostas:

46

O comando cdnã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 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 alterar o diretório de trabalho atual do processo de shell, por exemplo bash.

Se fosse um comando externo, um executável no caminho, a execução desse executável criaria um processo com seu próprio diretório de trabalho, sem influenciar o do shell atual. Mesmo que o comando externo altere seu diretório, essa alteração desaparece quando o processo externo é encerrado.

Comandos internos do shell

Portanto, não faz sentido executar um comando externo para a tarefa de cd. O comando cdprecisa aplicar uma alteração ao processo de shell atualmente em execução.

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

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

Sobre which

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

Alternativo type -a

Como alternativa which, você pode usar type -a; Pode ver comandos executáveis ​​e embutidos; Além disso, ele vê aliases e funções - também implementados 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
Volker Siegel
fonte
1
Ótima explicação!
SaltyNuts
3
Muito melhor do que a resposta atualmente aceita - isso explica por que cd um shell está embutido.
Lily Chung
28

cdé um shell mandatado pelo POSIX embutido:

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

  1. Se o nome do comando não contiver barras, ocorrerá a primeira etapa bem-sucedida da seguinte sequência:
    ...
    • 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 pesquisado usando o PATH ...

Embora isso não diga explicitamente que deve ser um built-in, a especificação continua a dizer, na descrição decd :

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

Do bashmanual :

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ê possa pensar em uma arquitetura em cdque não precise ser incorporada. No entanto, você precisa ver o que um built-in implica. Se você escrever um código especial no shell para executar algo em algum comando, estará chegando perto de ter um builtin. Quanto mais você faz, melhor é simplesmente ter um builtin.

Por exemplo, você poderia fazer com que o shell tivesse IPC para se comunicar com subprocessos, e haveria um cdprograma que verificaria a existência do diretório e se você tem permissão para acessá-lo e depois se comunica com o shell para solicitar que ele mude seu diretório. diretório. No entanto, você deverá verificar se o processo de comunicação com você é filho (ou criar meios especiais de comunicação apenas com filhos, como um descritor de arquivo especial, memória compartilhada etc.) e se o processo é de fato executando o cdprograma confiável ou outra coisa. Essa é uma lata inteira de vermes.

Ou você pode ter um cdprograma que faz a chdirchamada do sistema e inicia um novo shell com todas as variáveis ​​de ambiente atuais aplicadas ao novo shell e, em seguida, mata seu shell pai (de alguma forma) quando concluído. 1 1

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

Você se encontrará adicionando cada vez mais código para proteger esses métodos, e é consideravelmente mais simples simplesmente torná-lo um componente interno.


O fato de algo ser um executável não impede que ele seja incorporado. Caso em questão:

echo e test

echoe testsão utilitários mandados pelo POSIX ( /bin/echoe /bin/test). No entanto, quase todos os shell populares têm um embutido echoe test. Da mesma forma, killtambém está 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 outra coisa senão um builtin. Um deles é cd. Normalmente, se o caminho completo não for especificado e o nome do comando corresponder ao de um builtin, uma função adequada para esse comando será chamada. Dependendo do shell, o comportamento do interno e o do executável podem diferir (isso é particularmente um problema paraecho , que possui comportamentos bastante diferentes . Se você deseja ter certeza do comportamento, é preferível chamar o executável usando o comando caminho completo e defina variáveis ​​como POSIXLY_CORRECT(mesmo assim não há garantia real).

Tecnicamente, nada impede que você forneça um SO que também seja um shell e que tenha todos os comandos incorporados. Perto deste extremo está a BusyBox monolítica . O 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 Almquist Shell ( ash). Se você PATHcancelar a configuração enquanto estiver executando o BusyBox ash, os programas disponíveis no BusyBox ainda estarão acessíveis para você sem especificar a PATH. Eles se aproximam de serem embutidos no shell, exceto que o próprio shell é um tipo de embutido no BusyBox.


Estudo de caso: O Debian Almquist Shell ( dash)

Se você olhar para a dashfonte, o encadeamento de execução é mais ou menos assim (é claro, com funções adicionais envolvidas quando são usados ​​pipes e outras coisas):

maincmdloopevaltreeevalcommand

evalcommanddepois usa findcommandpara determinar qual é o comando. Se for 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 **). A evalbltinfunção chama (dependendo se o evalcomando interno é ou não) evalcmdou 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 linhas, portanto podem ser alterados sem aviso prévio.


1 Os sistemas POSIX possuem um cdexecutável .


Nota:

Existem muitas postagens excelentes no Unix e Linux que lidam com o comportamento do shell. Em particular:

Se você não notou um padrão nas perguntas listadas até agora, quase todas elas envolvem Stéphane Chazelas .

muru
fonte
4
Note que você pode obter o cdtexto de ajuda com help cd(mesma coisa para todos os comandos shell builtin)
Sylvain Pineau
@SylvainPineau, embora eu tenha vinculado ao manual do bash, esse conselho geralmente não é aplicável a outros shells, como o zsh.
Muru
Na verdade helpé um builtin bash (para zsh, é run-help cd)
Sylvain Pineau
A descrição vinculada da especificação POSIX não diz explicitamente que cddeve ser tão embutida no shell ... mas com base em como as propriedades do processo e sua transferência funcionam no UNIX cdcomo uma embutida no shell é a única implementação direta. Veja a resposta de Volker Siegel .
Pabouk
O @pabouk de fato (ele chama de utilitário) e continua dizendo: "Como o cd afeta o atual ambiente de execução do shell, ele é sempre fornecido como um shell comum incorporado".
muru
8

Você não pode encontrar um executável para cdporque não há nenhum.

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

Uwe Plonus
fonte
7

de man which:

que retorna os nomes de caminho dos arquivos (ou links) que seriam executados no ambiente atual, se seus argumentos fossem dados como comandos em um shell estritamente compatível com POSIX. Isso é feito pesquisando no PATH arquivos executáveis ​​que correspondem aos nomes dos argumentos. Não segue links simbólicos.

Como podemos ver na descrição de which, é apenas uma verificação PATH. Portanto, se você implementou alguns bash function, isso não mostrará nada. É melhor usar o typecomando junto com which.

Por exemplo, no lscomando 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

whichnã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 bash. Todos os 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.
c0rp
fonte
2
Mmmmmanwhich .
IQAndreas
1
Talvez deva ser enfatizado mais: não use which, use type.
Tripleee