cd
è un mandato POSIX shell integrata in :
Se un comando semplice genera un nome comando e un elenco opzionale di argomenti, devono essere eseguite le seguenti azioni:
- Se il nome del comando non contiene barre, si verifica il primo passaggio corretto nella sequenza seguente:
...
- Se il nome del comando corrisponde al nome di un'utilità elencata nella tabella seguente, tale utilità deve essere invocata.
...
cd
...
- Altrimenti, il comando deve essere cercato usando il PERCORSO ...
Anche se questo non dice esplicitamente che deve essere un built-in, la specifica prosegue dicendo nella descrizione dicd
:
Poiché cd influisce sull'ambiente di esecuzione della shell corrente, viene sempre fornito come un normale built-in della shell.
Dal bash
manuale :
I seguenti comandi incorporati della shell sono ereditati dalla Bourne Shell. Questi comandi sono implementati come specificato dallo standard POSIX.
...
cd
cd [-L|[-P [-e]]] [directory]
Suppongo che potresti pensare a un'architettura in cui cd
non debba essere incorporato. Tuttavia, devi vedere cosa implica un built-in. Se scrivi un codice speciale nella shell per fare qualcosa per un certo comando, ti stai avvicinando ad avere un builtin. Più fai, meglio è semplicemente avere un builtin.
Ad esempio, potresti avere la shell con IPC per comunicare con i sottoprocessi e ci sarebbe un cd
programma che verificherebbe l'esistenza della directory e se hai i permessi di accesso e quindi comunica con la shell per dirgli di cambiarne directory. Tuttavia, dovrai quindi verificare se il processo di comunicazione con te è un bambino (o creare mezzi speciali di comunicazione solo con i bambini, come un descrittore di file speciale, memoria condivisa, ecc.) E se il processo è in realtà eseguendo il fidatocd
programma o qualcos'altro. È un'intera lattina di vermi.
Oppure potresti avere un cd
programma che fa chdir
chiamare il sistema, e avvia una nuova shell con tutte le variabili d'ambiente correnti applicate alla nuova shell, e poi uccide la sua shell madre (in qualche modo) quando fatto. 1
Peggio ancora, potresti persino avere un sistema in cui un processo può alterare gli ambienti di altri processi (penso che tecnicamente puoi farlo con i debugger). Tuttavia un tale sistema sarebbe molto, molto vulnerabile.
Ti ritroverai ad aggiungere sempre più codice per proteggere tali metodi, ed è notevolmente più semplice semplicemente renderlo integrato.
Che qualcosa sia un eseguibile non impedisce che sia incorporato. Caso in questione:
echo
e test
echo
e test
sono utility con mandato POSIX ( /bin/echo
e /bin/test
). Eppure quasi ogni shell popolare ha un builtin echo
e test
. Allo stesso modo, kill
è anche incorporato che è disponibile come programma. Altri includono:
sleep
(non così comune)
time
false
true
printf
Tuttavia, ci sono alcuni casi in cui un comando non può essere altro che un builtin. Uno di questi è cd
. In genere, se non viene specificato il percorso completo e il nome del comando corrisponde a quello di un built-in, viene chiamata una funzione adatta a quel comando. A seconda della shell, il comportamento dell'integrato e quello dell'eseguibile possono differire (questo è particolarmente un problema perecho
, che ha comportamenti molto diversi . Se vuoi essere certo del comportamento, è preferibile chiamare l'eseguibile usando il percorso completo e impostare variabili come POSIXLY_CORRECT
(anche in questo caso non esiste una vera garanzia).
Tecnicamente non c'è nulla che ti impedisca di fornire un sistema operativo che è anche una shell e ha tutti i comandi come integrati. Vicino a questa estremità estrema c'è il monolitico BusyBox . BusyBox è un singolo binario, che (a seconda del nome con cui viene chiamato) può comportarsi come uno degli oltre 240 programmi , incluso Almquist Shell ( ash
). Se non si PATH
esegue l'impostazione durante l'esecuzione di BusyBox ash
, i programmi disponibili in BusyBox sono ancora accessibili all'utente senza specificare a PATH
. Si avvicinano all'essere builtin della shell, tranne per il fatto che la shell stessa è una sorta di builtin di BusyBox.
Se guardi l' dash
origine, il thread di esecuzione è qualcosa del genere (ovviamente, con funzioni aggiuntive coinvolte quando vengono utilizzate pipe e altre cose):
main
→ cmdloop
→ evaltree
→evalcommand
evalcommand
quindi utilizza findcommand
per determinare qual è il comando. Se è incorporato, quindi :
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
è un struct
( struct builtincmd
), di cui uno dei componenti è un puntatore a funzione, con una tipica firma di main
: (int, char **)
. La evalbltin
funzione chiama (a seconda che sia incorporato il eval
comando o meno) evalcmd
o questo puntatore alla funzione. Le funzioni effettive sono definite in vari file sorgente. echo
, ad esempio, è :
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;
}
Tutti i collegamenti al codice sorgente in questa sezione sono basati sul numero di riga, quindi possono cambiare senza preavviso.
1 I sistemi POSIX hanno un cd
eseguibile .
Nota a margine:
Ci sono molti post eccellenti su Unix e Linux che si occupano del comportamento della shell. In particolare:
Se non hai notato uno schema nelle domande elencate finora, quasi tutte coinvolgono Stéphane Chazelas .
type
comando