#! / Bin / sh è letto dall'interprete?


66

In basho sh, suppongo che tutto ciò che inizia con #è un commento .

Ma negli bashscript scriviamo:

#!/bin/bash

E negli script Python, c'è:

#!/bin/python

Questo significa che di #per sé è un commento, mentre #!non lo è?


1
E una volta che inizi a guardare i profili di Apparmor, vedrai #include. Anche lì, il #non è inteso come un commento.

4
@ vasa1 Ma il punto chiave che non è spesso apprezzato sulle linee hashbang all'inizio di script di shell è che essi sono commenti .
Eliah Kagan,

Risposte:


100

La #!riga viene utilizzata prima dell'esecuzione dello script, quindi ignorata quando viene eseguito lo script.

Stai chiedendo quale sia la differenza tra una linea shebang e un commento normale.

Una riga che inizia con #!è tanto un commento quanto qualsiasi altra riga che inizia con #. Questo è vero se #!è la prima riga del file o in qualsiasi altro luogo. #!/bin/sh ha un effetto , ma non viene letto dall'interprete stesso .

#non è un commento in tutti i linguaggi di programmazione ma, come sapete, è un commento nelle shell in stile Bourne tra cui she bash(così come la maggior parte delle shell in stile non Bourne, come csh). È anche un commento in Python . Ed è un commento in una varietà di file di configurazione che in realtà non sono affatto script (come /etc/fstab).

Supponiamo che uno script di shell inizi con #!/bin/sh. Questo è un commento e l'interprete (la shell) ignora tutto sulla riga dopo il #personaggio.

Lo scopo di una #!linea non è di fornire informazioni all'interprete. Lo scopo della #!linea è di dire al sistema operativo (o qualunque processo avvii l'interprete) cosa usare come interprete .

  • Se si richiama lo script come file eseguibile, ad esempio eseguendolo ./script.sh, il sistema consulta la prima riga per vedere se inizia #!, seguito da zero o più spazi, seguito da un comando. In tal caso, esegue quel comando con il nome dello script come argomento. In questo esempio, viene eseguito /bin/sh script.sh(o, tecnicamente, /bin/sh ./script.sh).

  • Se invochi lo script chiamando esplicitamente l'interprete, la #!linea non viene mai consultata. Quindi, se corri sh script.sh, la prima riga non ha alcun effetto. Se script2.shla prima riga è #!/usr/games/nibbles, in esecuzione sh script2.shnon tenterà di aprire lo script nibbles(ma lo ./script2.shfarà).

Noterai che in nessun caso l'estensione dello script ( .sh), se ne ha una, influisce sul modo in cui viene eseguita. In un sistema simile a Unix, ciò non influisce normalmente su come viene eseguito lo script. Su alcuni altri sistemi, come Windows, la #!linea shebang potrebbe essere completamente ignorata dal sistema e l'estensione potrebbe determinare ciò che esegue gli script. (Ciò non significa che devi fornire le estensioni degli script, ma è uno dei motivi per cui, se lo fai, dovrebbero essere corretti.)

#!è stato scelto per servire questo scopo proprio perché # inizia un commento. La #!linea è per il sistema, non per l'interprete, e dovrebbe essere ignorata dall'interprete.

Linea Shebang per script Bash

Tu (originariamente) hai detto che usi #!/bin/shper gli bashscript. Dovresti farlo solo se lo script non richiede alcuna delle bashestensioni - shdeve essere in grado di eseguire lo script. shnon è sempre un collegamento simbolico a bash. Spesso, anche su tutti i sistemi Debian e Ubuntu remoti di recente , shè un collegamento simbolico a dash.

Linea Shebang per script Python

Hai anche detto (nella prima versione della tua domanda, prima di modificarlo) che avvii i tuoi script Python con #!/bin/sh read by the interpretor. Se lo intendi letteralmente, allora dovresti assolutamente smettere di farlo. Se hello.pyinizia con quella riga, l'esecuzione ./hello.pyviene eseguita:

/bin/sh read by the interpretor hello.py

/bin/shproverà ad eseguire uno script chiamato read(con by the interpretor hello.pyi suoi argomenti), read(si spera) non verrà trovato e il tuo script Python non verrà mai visto da un interprete Python.

Se stai commettendo questo errore ma non hai il problema che sto descrivendo, probabilmente stai invocando i tuoi script Python specificando esplicitamente l'interprete (ad esempio, python hello.py), facendo sì che la prima riga venga ignorata. Quando distribuisci i tuoi script ad altri o li usi molto tempo dopo, potrebbe non essere chiaro che questo è necessario per farli funzionare. È meglio risolverli ora. O almeno rimuovere completamente la prima riga, in modo che quando non riescono a funzionare con ./il messaggio di errore abbia senso.

Per gli script Python, se sai dove si trova (o sta per essere) l'interprete Python, puoi scrivere la #!riga allo stesso modo:

#!/usr/bin/python

Oppure, se si tratta di uno script Python 3, è necessario specificare python3, poiché pythonè quasi sempre Python 2 :

#!/usr/bin/python3

Tuttavia, il problema è che mentre /bin/shdovrebbe sempre esistere, e /bin/bashquasi sempre esiste su sistemi in cui bashè presente il sistema operativo, Python può esistere in una varietà di luoghi.

Pertanto, molti programmatori Python usano questo invece:

#!/usr/bin/env python

(O #!/usr/bin/env python3per Python 3.)

Questo fa sì che lo script faccia affidamento envsull'essere nel "posto giusto" invece di fare affidamento sul pythonfatto di essere nel posto giusto. Questa è una buona cosa, perché:

  • envsi trova quasi sempre in /usr/bin.
  • Sulla maggior parte dei sistemi, qualunque python sia il tuo script da eseguire è quello che appare per primo nel file PATH. A partire hello.pyda #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, che è (praticamente) equivalente a running python hello.py.

Il motivo per cui non #!pythonè possibile utilizzare è che:

  • Volete che l'interprete specificato sia dato da un percorso assoluto (cioè che inizia con /).
  • Il processo di chiamata verrà eseguito python nella directory corrente . Cercare il percorso quando il comando non contiene una barra è un comportamento specifico della shell.

Occasionalmente uno script Python o altro che non è uno script shell avrà una riga shebang che inizia con #!/bin/sh ...dove si ...trova un altro codice. Questo a volte è corretto, perché ci sono alcuni modi per invocare la shell ( sh) compatibile con Bourne con argomenti per farlo invocare un interprete Python. (Probabilmente uno degli argomenti conterrà python.) Tuttavia, per la maggior parte degli scopi, #!/usr/bin/env pythonè più semplice, più elegante e più probabile che funzioni nel modo desiderato.

Linee Shebang in altre lingue

Molti linguaggi di programmazione e di scripting, e alcuni altri formati di file, usano #come commento. Per ognuno di essi, un file nella lingua può essere eseguito da un programma che lo prende come argomento specificando il programma sulla prima riga dopo #!.

In alcuni linguaggi di programmazione, #normalmente non è un commento, ma come caso speciale la prima riga viene ignorata se inizia con #!. Questo facilita l'uso della #!sintassi anche se #altrimenti non fa una riga un commento.

Linee Shebang per file che non funzionano come script

Sebbene sia meno intuitivo, qualsiasi file il cui formato di file può contenere una prima riga che inizia con #!seguito dal percorso completo di un eseguibile può avere una riga shebang. Se lo fai, e il file è contrassegnato come eseguibile, puoi eseguirlo come un programma ... facendolo aprire come un documento.

Alcune applicazioni usano questo comportamento intenzionalmente. Ad esempio, in VMware, i .vmxfile definiscono le macchine virtuali. Puoi "eseguire" una macchina virtuale come se fosse uno script perché questi file sono contrassegnati come eseguibili e hanno una linea shebang che li fa aprire in un'utilità VMware.

Linee Shebang per file che non funzionano come script ma che agiscono comunque come script

rmrimuove i file. Non è un linguaggio di scripting. Tuttavia, #!/bin/rmè possibile eseguire un file che viene avviato e contrassegnato come eseguibile e, quando lo si esegue, rmviene richiamato su di esso, eliminandolo.

Questo è spesso concettualizzato come "il file si elimina da solo". Ma il file non è davvero in esecuzione. Questo è più simile alla situazione sopra descritta per i .vmxfile.

Tuttavia, poiché la #!riga facilita l'esecuzione di un semplice comando (inclusi gli argomenti della riga di comando), è possibile eseguire alcuni script in questo modo. Come semplice esempio di uno "script" più sofisticato di #!/bin/rm, considera:

#!/usr/bin/env tee -a

Questo prende l'input dell'utente in modo interattivo, lo fa ritornare all'utente riga per riga e lo aggiunge alla fine del file "script".

Utile? Non molto. Concettualmente interessante? Totalmente! Sì. (Un po '.)

Concetti di programmazione / scripting concettualmente simili (solo per divertimento)


@Rinzwind Thx! (A proposito, questa risposta non ha origine altrove, se è quello che ti stai chiedendo.)
Eliah Kagan

@Rinzwind Non ti preoccupare, con 8 voti dopo 1 ora è probabile che aumenti molto di più :-)
Guntbert

1
Se viene sempre ignorato, allora cosa fa la -xbandiera Pythons ?
Gerrit,

4
@gerrit Buona domanda. In qualsiasi lingua in cui l'interprete / il compilatore segnala messaggi con numeri di riga, i contenuti dei commenti vengono ignorati, ma le righe dei commenti vengono comunque conteggiate . L'aggiunta di un commento o di una riga vuota prima di una riga di codice comporta comunque l'incremento del numero di riga su quella riga di codice. -x"salta [s] la prima riga ..." la 2a riga viene numerata 1anziché 2la 3a riga 2anziché 3, ecc. Ecco perché non dovresti usare quella bandiera. ;) -xè per lo scripting su sistemi operativi non Unix che hanno una sintassi simile a shebang che non inizia #(quindi non un commento Python).
Eliah Kagan,

4
In Perl, se l'interprete viene avviato direttamente ( perl script.plvs ./script.pl), allora l'interprete sarà leggere la linea shebang per analizzare le bandiere, come -w. Tuttavia, non è consigliabile fare affidamento su questa funzione.
Smetti di fare del male a Monica il

7

Uno shebang è la sequenza di caratteri costituita dal segno del numero di caratteri e dal punto esclamativo (ad esempio "#!") Quando si presenta come i due caratteri iniziali sulla riga iniziale di uno script.

Sotto i sistemi operativi * nix, quando viene eseguito uno script che inizia con uno shebang, il programma di caricamento programma analizza il resto della riga iniziale dello script come una direttiva interprete; viene invece eseguito il programma interprete specificato, passando ad esso come argomento il percorso inizialmente utilizzato durante il tentativo di eseguire lo script. Ad esempio, se uno script è denominato con il percorso "path / to / your-script" e inizia con la seguente riga:

#!/bin/sh

quindi al programma di caricamento del programma viene richiesto di eseguire il programma "/ bin / sh", ad esempio la shell Bourne o una shell compatibile, passando "path / to / your-script" come primo argomento.

Di conseguenza, uno script viene chiamato con il percorso "path / to / python-script" e inizia con la seguente riga:

#!/bin/python

quindi al programma caricato viene richiesto di eseguire il programma "/ bin / python" invece ad es. interprete Python, passando "path / to / python-script" come primo argomento.

In breve "#" commenterà una riga mentre la sequenza di caratteri "#!" che si presentano come i primi due caratteri sulla riga iniziale di uno script hanno significato delineato come sopra.

Per i dettagli, vedi Perché alcuni script iniziano con #! ...?

Fonte: alcune sezioni di questa risposta sono derivate (con lievi modifiche) da Shebang (Unix) su Wikipedia in inglese (dai collaboratori di Wikipedia ). Questo articolo è concesso in licenza in base a CC-BY-SA 3.0 , come il contenuto dell'utente qui su AU, quindi questa derivazione è consentita con attribuzione.


4

#!viene chiamato shebangquando si presenta come i due caratteri iniziali sulla riga iniziale di uno script. Viene utilizzato negli script per indicare un interprete per l'esecuzione. Il shebangè per il sistema operativo (kernel), non per la shell; quindi non verrà interpretato come un commento.

Per gentile concessione: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

In generale, se un file è eseguibile, ma in realtà non è un programma eseguibile (binario), e tale linea è presente, il programma specificato dopo #! viene avviato con lo scriptname e tutti i suoi argomenti. Questi due personaggi # e! devono essere i primi due byte nel file!

Informazioni dettagliate: http://wiki.bash-hackers.org/scripting/basics#the_shebang


0

No, viene utilizzato solo dalla execchiamata di sistema del kernel Linux e trattato come un commento dall'interprete

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 primi 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 .

Tuttavia , non credo che POSIX specifichi shebang: https://unix.stackexchange.com/a/346214/32558 , anche se menzionato nelle sezioni logiche e nel modulo "se gli script eseguibili sono supportati dal sistema qualcosa potrebbe accadere".

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.