Perché ls
richiede un processo separato per la sua esecuzione? Conosco il motivo per cui comandi come cd
non possono essere eseguiti dal meccanismo di fork, ma c'è del male se ls
viene eseguito senza fork?
Perché ls
richiede un processo separato per la sua esecuzione? Conosco il motivo per cui comandi come cd
non possono essere eseguiti dal meccanismo di fork, ma c'è del male se ls
viene eseguito senza fork?
Risposte:
La risposta è più o meno ls
un eseguibile esterno. Puoi vedere la sua posizione eseguendo type -p ls
.
Perché non è ls
integrato nella shell, quindi? Bene, perché dovrebbe essere? Il compito di una shell non è comprendere tutti i comandi disponibili, ma fornire un ambiente in grado di eseguirli. Alcune shell moderne hanno echo
, printf
e il loro simile come builtin, che tecnicamente non devono essere builtin, ma sono fatti per motivi di prestazioni quando vengono eseguiti ripetutamente (principalmente in loop stretti). Senza renderli predefiniti, la shell dovrebbe eseguire il fork ed eseguire un nuovo processo per ogni chiamata, che potrebbe essere estremamente lento.
Per lo meno, l'esecuzione ls
, un eseguibile esterno, richiede l'esecuzione di una delle chiamate di sistema della famiglia exec. Si potrebbe fare questo senza forking, ma sarebbe sostituire la shell primario che si sta utilizzando. Puoi vedere cosa succede in quell'istanza procedendo come segue:
exec ls; echo "this never gets printed"
Poiché l'immagine di processo della shell viene sostituita, la shell corrente non è più accessibile dopo aver fatto ciò. Affinché la shell possa continuare a funzionare dopo aver eseguito ls, il comando dovrebbe essere integrato nella shell.
Forking consente la sostituzione di un processo che non è la shell principale, il che significa che è possibile continuare a eseguire la shell in seguito.
echo
, printf
e così via
cd
non è un eseguibile esterno?
cd
eseguibile in sistemi operativi POSIX-compliant ( vedi qui ). Se vuoi effettivamente chdir () nel processo corrente, però, devi averlo incorporato nella shell.
Il Manuale di riferimento di Bash afferma:
I comandi integrati sono necessari per implementare funzionalità impossibili o scomode da ottenere con utility separate.
Cioè, le shell sono progettate per includere comandi integrati solo se:
Il ls
comando non soddisfa nessuno dei requisiti di cui sopra.
Tuttavia , qui non ci sono vincoli di programmazione che potrebbero impedire ls
l'implementazione come built-in, che si sta eseguendo nello stesso processo dell'interprete bash. Le ragioni di progettazione per i comandi che non vengono impiantati come built-in della shell sono:
Per quanto riguarda il primo motivo: vuoi che la shell sia il più indipendente e resiliante possibile. Non vuoi che la shell rimanga bloccata su ls
un mount NFS che "non risponde ancora provando".
Per quanto riguarda il secondo motivo: in molti casi potresti voler utilizzare una shell per un sistema che utilizza Busybox o un altro file system con ls
un'implementazione diversa . O persino utilizzare la stessa sorgente di shell nei sistemi operativi che hanno ls
implementazioni diverse .
Per quanto riguarda la terza ragione: per espressioni come find . -type d | xargs ls -lad
questa sarebbe difficile o impossibile implementare ls
lo stesso processo dell'interprete della shell.
Per quanto riguarda il quarto motivo: alcuni ls
comandi possono richiedere molto tempo per il completamento. Nel frattempo potresti volere che la shell continui a fare qualcos'altro.
Nota: vedi questo utile post di Warren Young in risposta a una domanda simile.
ls
in un processo esterno. Potrebbe essere fatto, ma sarebbe complicato.
bash
uscita alias | grep ls
. inputcat /etc/passwd | while read a; do echo "$a"; done
ls
non richiede un processo separato. Pochissimi comandi in realtà richiedono un processo separato: solo quelli che devono cambiare i privilegi.
Di norma, le shell implementano i comandi come builtin solo quando tali comandi devono essere implementati come builtin. Comandi come alias
, cd
, exit
,export
, jobs
, ... necessità di leggere o modificare alcune stato interno del guscio, e pertanto non può essere programmi separati. I comandi che non hanno tali requisiti possono essere comandi separati; in questo modo, possono essere chiamati da qualsiasi shell o altro programma.
Guardando l'elenco dei builtin in bash, solo i seguenti builtin possono essere implementati come comandi separati. Per alcuni, ci sarebbe una leggera perdita di funzionalità.
command
- ma perderebbe la sua utilità in situazioni in cui PATH
potrebbe non essere impostato correttamente e lo script sta usando command
come parte della sua configurazione.echo
- è integrato per l'efficienza.help
- potrebbe utilizzare un database separato, ma l'incorporamento del testo di aiuto nell'eseguibile della shell ha il vantaggio di rendere autonomo l'eseguibile della shell.kill
- ci sono due vantaggi nell'avere un builtin: può riconoscere le designazioni di lavoro oltre agli ID di processo e può essere utilizzato anche quando non ci sono risorse sufficienti per avviare un processo separato.printf
- per lo stesso motivo di echo
, e anche per supportare l' -v
opzione di mettere l'output in una variabile.pwd
- il built-in offre la capacità aggiuntiva di tracciare la directory corrente logica (lasciando intatti i collegamenti simbolici invece di espanderli).test
- è integrato per l'efficienza (e bash fa anche un po 'di magia con i file chiamati /dev/fd/…
su alcuni sistemi operativi).Alcune shell offrono un numero significativo di builtin aggiuntivi. C'è sash , che è una shell progettata per essere un binario autonomo per le riparazioni di emergenza (quando alcuni comandi esterni potrebbero non essere utilizzabili). Ha un built-in ls
, chiamato -ls
, così come altri strumenti come -grep
e -tar
. I built-in di Sash hanno meno capacità rispetto ai comandi completi. Zsh offre alcuni builtin simili nel suo modulo zsh / files . Non ha ls
, ma espansione jolly ( echo *
) e zstat
può svolgere una funzione simile.
Penso che qualcosa che manca alla gente qui sia la complessità di taglio del ls
programma GNU su Linux. Confrontando la dimensione eseguibile di ls
con bash
e dash
shell sul mio sistema Debian, vediamo che è abbastanza grande:
graeme@graeme:~$ ls -lh /bin/{ls,bash,dash}
-rwxr-xr-x 1 root root 953K Mar 30 2013 /bin/bash
-rwxr-xr-x 1 root root 115K Dec 25 20:25 /bin/dash
-rwxr-xr-x 1 root root 108K Jul 20 22:52 /bin/ls
Includere una funzionalità ls
completa come la versione GNU bash
aumenterebbe la dimensione dell'eseguibile del 10%. Ha quasi le stesse dimensioni dell'intero dash
guscio!
La maggior parte dei builtin della shell sono scelti perché si integrano con la shell in un modo che gli eseguibili esterni non possono (la domanda sottolinea cd
, ma un altro esempio è la versione bash kill
dell'integrazione con il controllo del lavoro bash) o perché sono comandi molto semplici da implementare, dando un grande guadagno rispetto alla dimensione (true
e false
sono quasi semplici come si arriva).
GNU ls
ha avuto un lungo ciclo di sviluppo e implementa le opzioni possibili per personalizzare quali / come visualizzare i risultati. L'uso di un predefinito per impostazione predefinita perderebbe questa funzionalità o aumenterebbe significativamente la complessità e le dimensioni della shell.
Questo fa quello che stai cercando:
printf "%s\n" *
Inoltre puoi archiviare i nomi dei file in array:
files=(`printf "%s\n" *`) #items are separated by whitespace
echo ${#files[*]} files
for index in ${!a[*]}
do printf "%d: %s\n" $index ${a[$index]};
done
Ma non importa degli spazi nei nomi.
Passa alla variabile e si preoccupa degli spazi:
printf "%s\n" * | while read a; do echo $a; done
ls
è un programma esternoecho *
oecho * .*
(a seconda delle opzioni della shell) fa un buon lavoro di elencare i file senza biforcarsi.