Lo shebang determina la shell che esegue lo script?


84

Questa potrebbe essere una domanda sciocca, ma lo faccio ancora. Se ho dichiarato uno shebang

#!/bin/bash 

all'inizio di my_shell_script.sh, quindi devo sempre invocare questo script usando bash

[my@comp]$bash my_shell_script.sh

o posso usare ad es

[my@comp]$sh my_shell_script.sh

e il mio script determina la shell in esecuzione usando shebang? Sta succedendo lo stesso con kshShell? Sto usando AIX.


6
c'è un po 'di confusione da parte tua: quando fai "_some_shell some_script" inizia _some_shell e gli chiede di interpretare some_script. Quindi no, se fai "sh my_shell_script.sh" non interpreterà lo shebang, ma interpreterà invece lo script in sh. Per usare lo shebang: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Olivier Dulac il

Risposte:


117

La faccenda #! è un'istanza leggibile di un numero magico costituito dalla stringa di byte 0x23 0x21, che viene utilizzato dalla exec()famiglia di funzioni per determinare se il file da eseguire è uno script o un file binario. Quando lo shebang è presente, exec()eseguirà invece l'eseguibile specificato dopo lo shebang.

Si noti che ciò significa che se si richiama uno script specificando l'interprete sulla riga di comando, come avviene in entrambi i casi indicati nella domanda, exec()verrà eseguito l'interprete specificato sulla riga di comando, non verrà nemmeno guardato lo script.

Quindi, come altri hanno notato, se si desidera exec()invocare l'interprete specificato sulla riga shebang, lo script deve avere il bit eseguibile impostato e invocato come ./my_shell_script.sh.

Il comportamento è facile da dimostrare con il seguente script:

#!/bin/ksh
readlink /proc/$$/exe

Spiegazione:

  • #!/bin/kshdefinisce kshl'interprete.

  • $$ contiene il PID del processo corrente.

  • /proc/pid/exe è un link simbolico all'eseguibile del processo (almeno su Linux; su AIX, /proc/$$/object/a.out è un link all'eseguibile).

  • readlink produrrà il valore del collegamento simbolico.

Esempio:

Nota : Sto dimostrando questo su Ubuntu, in cui la shell di default /bin/shè un link simbolico a precipitare cioè /bin/dashed /bin/kshè un link simbolico a /etc/alternatives/ksh, che a sua volta è un link simbolico a /bin/pdksh.

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

grazie Thomas per questa risposta. Fingiamo di lanciare lo script come processo figlio da Node.js o Java o altro. Possiamo avviare un processo "exec" e quindi exec eseguirà lo script della shell? Chiedo perché sto cercando risposte a questa domanda: stackoverflow.com/questions/41067872/…
Alexander Mills

1
@AlexanderMills Il exec()riferimento a questa risposta è una chiamata di sistema, il comando execè un shell incorporato, motivo per cui non è possibile richiamare un exec programma da Node.js o Java. Tuttavia, qualsiasi comando di shell richiamato ad es. Runtime.exec()In Java viene infine elaborato dalla exec()chiamata di sistema.
Thomas Nyman,

Eh, sì, ho familiarità con l'API Java che hai appena menzionato, mi chiedo se c'è un modo per invocare in qualche modo la chiamata exec () di livello inferiore da Node.js
Alexander Mills,

@AlexanderMills Immagino child_process.{exec(),execFile(),spawn()} tutto verrebbe implementato usando la C exec()(attraverso process).
Thomas Nyman,

10

Sì lo fa. A proposito, non è una domanda sciocca. Un riferimento per la mia risposta è qui . Avvio di uno script con #!

  • Si chiama shebang o "bang" line.

  • Non è altro che il percorso assoluto per l'interprete Bash.

  • È composto da un segno numerico e da un punto esclamativo (#!), Seguito dal percorso completo dell'interprete come / bin / bash.

    Tutti gli script in Linux vengono eseguiti utilizzando l'interprete specificato su una prima riga Quasi tutti gli script bash spesso iniziano con #! / Bin / bash (supponendo che Bash sia stato installato in / bin) Ciò garantisce che Bash verrà utilizzato per interpretare lo script, anche se viene eseguito sotto un'altra shell. Lo shebang è stato introdotto da Dennis Ritchie tra la versione 7 Unix e 8 presso i Bell Laboratories. Fu quindi aggiunto anche alla linea BSD di Berkeley.

Ignora una linea dell'interprete (shebang)

Se non si specifica una linea di interprete, l'impostazione predefinita è di solito / bin / sh. Tuttavia, si consiglia di impostare #! / Bin / bash line.


3
Per elaborare, il kernel sa solo come eseguire binari collegati staticamente e dove trovare informazioni sull'interprete per altri (un campo speciale nel binario o la riga shebang). In genere eseguire uno script di shell significa seguire la riga shebang sulla shell e quindi seguire il campo DT_INTERP nel binario della shell verso il linker dinamico.
Simon Richter,

5
Si noti inoltre che ciò non si limita agli script di shell. Tutti i file di script basati su testo usano questo. Ad esempio, #!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyun'altra voce shebang comune utilizzata per supportare più sistemi è quella di utilizzare env per individuare l'interprete che si desidera utilizzare, ad esempio#!/usr/bin/env perl #!/usr/bin/env python
sambler

@sambler parlando di env, quale dovrebbe essere effettivamente preferito? Python e Perl usano spesso env, mentre su shellscripts, questo è spesso omesso e shebang punta alla shell in questione.
polemon

1
@polemon meno dei quali è preferito e più su quali percorsi variano. Le shell di base si trovano nello stesso percorso su tutti i sistemi. Versioni aggiornate di perl e python possono essere installate in posizioni diverse su sistemi diversi, quindi l'uso di env consente allo stesso shebang di funzionare sempre, motivo per cui env viene utilizzato più con script perl e python che con shell.
sambler

envtrovare un programma in $ PATH è un po 'un trucco. Non imposta variabili d'ambiente come suggerisce il nome. $ PATH potrebbe essere un risultato diverso per utenti diversi. Ma aiuta gli script a funzionare senza modifiche su sistemi che mettono un interprete perl ragionevole in un posto strano.
John Mahowald,

4

La execchiamata di sistema del kernel Linux comprende #!nativamente shebangs ( )

Quando fai su bash:

./something

su Linux, questo chiama la execchiamata di sistema con il percorso ./something.

Questa riga del kernel viene chiamata sul file passato a exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Legge i primissimi byte del file e li confronta con #!.

Se il confronto è vero, il resto della riga viene analizzato dal kernel Linux, che effettua un'altra execchiamata con percorso /usr/bin/env pythone file corrente come primo argomento:

/usr/bin/env python /path/to/script.py

e questo funziona con qualsiasi linguaggio di scripting che utilizza #come carattere di commento.

E sì, puoi fare un ciclo infinito con:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash riconosce l'errore:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! sembra essere leggibile dall'uomo, ma non è necessario.

Se il file si avviava con byte diversi, la execchiamata di sistema utilizzava un gestore diverso. L'altro gestore incorporato più importante è per i file eseguibili ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 che verifica la presenza di byte 7f 45 4c 46(che risulta essere umano leggibile per .ELF). Confermiamo leggendo i primi 4 byte di /bin/ls, che è un eseguibile ELF:

head -c 4 "$(which ls)" | hd 

produzione:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Quindi quando il kernel vede quei byte, prende il file ELF, lo mette in memoria correttamente e avvia un nuovo processo con esso. Vedi anche: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Infine, puoi aggiungere i tuoi gestori shebang con il binfmt_miscmeccanismo. Ad esempio, è possibile aggiungere un gestore personalizzato per i .jarfile . Questo meccanismo supporta anche i gestori per estensione del file. Un'altra applicazione è eseguire in modo trasparente eseguibili di un'architettura diversa con QEMU .

Non penso che POSIX specifichi shebangs, tuttavia: https://unix.stackexchange.com/a/346214/32558 , anche se lo menziona nelle sezioni logiche e nella forma "se gli script eseguibili sono supportati dal sistema qualcosa può succedere ".


1
L'esecuzione ./somethingda una shell non passerà al percorso completoexec , ma esattamente al percorso inserito. Puoi correggerlo nella tua risposta? Fai echo "$0"nella tua sceneggiatura e vedrai che è così.
AndiDog,

2

In effetti, se lo prendi di conseguenza, l'eseguibile annotato nella riga shebang è solo un eseguibile. Ha senso usare un interprete di testo come eseguibile, ma non è necessario. Solo per chiarimenti e dimostrazioni, ho fatto un test piuttosto inutile:

#!/bin/cat
useless text
more useless text
still more useless text

Nominato il file test.txt e impostare il bit exectuable chmod u+x test.txt, quindi "chiamato" che: ./test.txt. Come previsto, viene emesso il contenuto del file. In questo caso, il gatto non ignora la linea shebang. Emette semplicemente tutte le linee. Qualsiasi utile interprete dovrebbe quindi essere in grado di ignorare questa riga di shebang. Per bash, perl e PHP, è semplicemente una riga di commento. Quindi sì, questi ignorano la linea shebang.


-1

Da quello che ho raccolto, ogni volta che un file ha un bit eseguibile impostato e viene invocato, il kernel analizza l'intestazione del file al fine di determinare come procedere (per quanto ne so, è possibile aggiungere gestori personalizzati per formati di file personalizzati tramite LKM). Se il file sembra essere un file di testo con un #! all'inizio, la sua esecuzione viene inviata a un altro eseguibile (di solito una sorta di shell), un percorso a cui deve essere specificato direttamente dopo detto shebang, nella stessa riga. Il kernel procede quindi all'esecuzione della shell e passa il file per gestirlo.

In breve, non importa con quale shell invochi lo script: il kernel invierà l'esecuzione a quella appropriata in entrambi i modi.


4
C'è una marcata differenza tra bash ./myscript.she ./myscript.sh.
un CVn del

Cosa intendi con "differenza marcata"?
jrara,

3
@jrara Vedi la mia risposta, l'affermazione che "non importa con quale shell invochi lo script" semplicemente non è vera.
Thomas Nyman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.