Quando inizia uno script di shell #!
, quella prima riga è un commento per quanto riguarda la shell. Tuttavia i primi due caratteri sono significativi per un'altra parte del sistema: il kernel. I due personaggi #!
sono chiamati shebang . Per capire il ruolo dello shebang, devi capire come viene eseguito un programma.
L'esecuzione di un programma da un file richiede un'azione da parte del kernel. Questo viene fatto come parte della execve
chiamata di sistema. Il kernel deve verificare le autorizzazioni del file, liberare le risorse (memoria, ecc.) Associate al file eseguibile attualmente in esecuzione nel processo di chiamata, allocare le risorse per il nuovo file eseguibile e trasferire il controllo al nuovo programma (e altre cose che Non menzionerò). La execve
chiamata di sistema sostituisce il codice del processo attualmente in esecuzione; c'è una chiamata di sistema separata fork
per creare un nuovo processo.
Per fare ciò, il kernel deve supportare il formato del file eseguibile. Questo file deve contenere codice macchina, organizzato in modo da comprendere il kernel. Uno script di shell non contiene codice macchina, quindi non può essere eseguito in questo modo.
Il meccanismo shebang consente al kernel di differire il compito di interpretare il codice su un altro programma. Quando il kernel vede che inizia il file eseguibile #!
, legge i seguenti caratteri e interpreta la prima riga del file (meno lo #!
spazio iniziale e facoltativo) come un percorso verso un altro file (più argomenti, che non tratterò qui ). Quando viene detto al kernel di eseguire il file /my/script
e vede che il file inizia con la linea #!/some/interpreter
, il kernel viene eseguito /some/interpreter
con l'argomento /my/script
. Sta quindi /some/interpreter
a decidere che /my/script
è un file di script che dovrebbe eseguire.
Cosa succede se un file non contiene né un codice nativo in un formato comprensibile per il kernel e non inizia con uno shebang? Bene, allora il file non è eseguibile e la execve
chiamata di sistema non riesce con il codice di errore ENOEXEC
(errore di formato eseguibile).
Questa potrebbe essere la fine della storia, ma la maggior parte delle shell implementa una funzionalità di fallback. Se il kernel ritorna ENOEXEC
, la shell esamina il contenuto del file e controlla se sembra uno script di shell. Se la shell pensa che il file assomigli a uno script di shell, lo esegue da solo. I dettagli di come ciò dipende dalla shell. Puoi vedere qualcosa di ciò che sta accadendo aggiungendo ps $$
nel tuo script e altro osservando il processo in strace -p1234 -f -eprocess
cui 1234 è il PID della shell.
In bash, questo meccanismo di fallback è implementato chiamando fork
ma non execve
. Il processo bash figlio cancella il suo stato interno da solo e apre il nuovo file di script per eseguirlo. Pertanto, il processo che esegue lo script utilizza ancora l'immagine del codice bash originale e gli argomenti della riga di comando originali passati quando è stato richiamato originariamente bash. ATT ksh si comporta allo stesso modo.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash, al contrario, reagisce ENOEXEC
chiamando /bin/sh
il percorso dello script passato come argomento. In altre parole, quando si esegue uno script shebangless dal trattino, si comporta come se lo script avesse una riga shebang #!/bin/sh
. Mksh e zsh si comportano allo stesso modo.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh