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:
- 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 ...
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.
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 .