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:
- 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 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ê possa pensar em uma arquitetura em cd
que 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 cd
programa 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 cd
programa confiável ou outra coisa. Essa é uma lata inteira de vermes.
Ou você pode ter um cd
programa que faz a chdir
chamada 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
echo
e test
são utilitários mandados pelo POSIX ( /bin/echo
e /bin/test
). No entanto, quase todos os shell populares têm um embutido echo
e test
. Da mesma forma, kill
també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ê PATH
cancelar 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.
Se você olhar para a dash
fonte, o encadeamento de execução é mais ou menos assim (é claro, com funções adicionais envolvidas quando são usados pipes e outras coisas):
main
→ cmdloop
→ evaltree
→evalcommand
evalcommand
depois usa findcommand
para 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 evalbltin
função chama (dependendo se o eval
comando interno é 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 linhas, portanto podem ser alterados sem aviso prévio.
1 Os sistemas POSIX possuem um cd
executá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 .
type
comando