Perché il comportamento della sintassi `#!` Non è specificato da POSIX?


17

Dalla pagina Lingua comandi Shell delle specifiche POSIX:

Se la prima riga di un file di comandi shell inizia con i caratteri "#!", I risultati non sono specificati.

Perché il comportamento di #!POSIX non è specificato? Trovo sconcertante che qualcosa di così portatile e ampiamente utilizzato avrebbe un comportamento non specificato.


1
Gli standard lasciano le cose non specificate per non limitare le implementazioni a comportamenti particolari. Ad esempio, un "login" è "L'attività non specificata mediante la quale un utente ottiene l'accesso al sistema".
Kusalananda

2
Poiché POSIX non specifica percorsi eseguibili, una linea shebang è comunque intrinsecamente non portatile; Non sono sicuro che si otterrebbe molto specificandolo a prescindere.
Michael Homer,

1
@MichaelHomer, sicuramente no? Lo standard potrebbe specificare che la riga contiene un percorso da utilizzare per l'interprete, anche senza dire quale dovrebbe essere quel percorso.
ilkkachu,

1
@HaroldFischer Tranne che non è interpretato dalla shell, è interpretato dal kernel del sistema operativo (fatto almeno su Linux, che può effettivamente disabilitare questo supporto durante il tempo di compilazione), o qualunque libreria implementa la exec()funzione. Quindi il controllo su più shell non ti dice quanto sia portatile.
Austin Hemmelgarn,

2
@HaroldFischer Inoltre, anche tra i sistemi operativi compatibili con POSIX il comportamento non è coerente. Linux e macOS si comportano diversamente: Linux non tokenizza completamente la linea shebang per spazi. macOS non consente all'interprete di script di essere un altro script. Vedi anche en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Risposte:


21

Penso principalmente perché:

  • il comportamento varia notevolmente tra implementazione. Vedi https://www.in-ulm.de/~mascheck/various/shebang/ per tutti i dettagli.

    Ora potrebbe tuttavia specificare un sottoinsieme minimo della maggior parte delle implementazioni simili a Unix: come #! *[^ ]+( +[^ ]+)?\n(con solo caratteri del set di caratteri del nome file portatile impostato in una o due parole) in cui la prima parola è un percorso assoluto per un eseguibile nativo, la cosa non è troppo lungo e comportamento non specificato se l'eseguibile è setuid / setgid e l'implementazione ha definito se il percorso dell'interprete o il percorso dello script viene passato argv[0]all'interprete.

  • POSIX non specifica comunque il percorso degli eseguibili. Diversi sistemi hanno utilità pre-POSIX in /bin/ /usr/bine hanno le utilità POSIX da qualche altra parte (come su Solaris 10 in cui /bin/shè presente una shell Bourne e in quella POSIX /usr/xpg4/bin; Solaris 11 l'ha sostituita con ksh93 che è più conforme POSIX, ma la maggior parte delle altre gli strumenti /binsono ancora antichi non POSIX). Alcuni sistemi non sono POSIX ma hanno una modalità / emulazione POSIX. Tutto ciò che POSIX richiede è che ci sia un ambiente documentato in cui un sistema si comporta POSIX.

    Vedi Windows + Cygwin per esempio. In realtà, con Windows + Cygwin, lo she-bang viene onorato quando uno script viene invocato da un'applicazione cygwin, ma non da un'applicazione Windows nativa.

    Quindi, anche se POSIX specificasse il meccanismo shebang, non potrebbe essere usato per scrivere script POSIX sh/ sed/ awk... (si noti inoltre che il meccanismo shebang non può essere usato per scrivere script sed/ affidabili awkin quanto non consente il passaggio di una fine dell'opzione marcatore).

Ora il fatto che non sia specificato non significa che non puoi usarlo (beh, dice che non dovresti iniziare con la prima riga #!se ti aspetti che sia solo un commento regolare e non un botto), ma che POSIX non offre alcuna garanzia in tal caso.

Nella mia esperienza, l'uso di shebang ti dà più garanzia di portabilità rispetto all'uso del modo POSIX di scrivere script di shell: lascia perdere il botto, scrivi lo script nella shsintassi POSIX e spera che qualunque cosa invochi lo script invochi un POSIX conforme sh, che è bene se sai che lo script verrà invocato nell'ambiente giusto dallo strumento giusto, ma non altrimenti.

Potrebbe essere necessario fare cose come:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Se vuoi essere portabile su Windows + Cygwin, potresti dover nominare il tuo file con un'estensione .bato .ps1e usare qualche trucco simile per cmd.exeo powershell.exeinvocare il cygwin shsullo stesso file.


È interessante notare che dal numero 5 : "Il costrutto #! È riservato alle implementazioni che desiderano fornire tale estensione. Un'applicazione portatile non può usare #! Come prima riga di uno script di shell; potrebbe non essere interpretato come un commento."
Muru,

@muru Se lo script fosse veramente portatile, su un vero sistema POSIX che eseguiva un POSIX sh, non avrebbe bisogno di una linea hashbang come sarebbe eseguita da POSIX sh.
Kusalananda

1
@Kusalananda è vero solo se execlpo execvpsono stati usati, giusto? Se dovessi usare execve, si tradurrebbe in ENOEXEC?
Muru,

9

Il comportamento sembra coerente tra tutte le shell di reclami POSIX. Non vedo la necessità della necessità di spazio di manovra qui.

Non stai guardando abbastanza in profondità.

Negli anni '80, questo meccanismo non era di fatto standardizzato. Sebbene Dennis Ritchie lo avesse implementato, tale implementazione non aveva raggiunto il pubblico nel lato AT&T dell'universo. In effetti era disponibile solo pubblicamente e conosciuto in BSD; con script shell eseguibili non disponibili su Unix AT&T. Quindi non era ragionevole standardizzarlo. La situazione è esemplificata da questo documento contemporaneo, uno dei tanti:

Si noti che BSD consente #! interpreterdi eseguire direttamente i file che iniziano con , mentre SysV consente di eseguire direttamente solo i file a.out. Ciò significa che exec…()potrebbe essere necessario modificare un'istanza di una delle routine in un programma BSD in SysV per eseguire invece l'interprete (tipicamente /bin/sh) per quel programma.
- Stephen Frede (1988). "Programmazione su System X versione Y". Newsletter del gruppo di utenti australiani Unix Systems . Volume 9. Numero 4. p. 111.

Un punto importante qui è che stai guardando le shell, mentre l'esistenza di script di shell eseguibili è in realtà una questione di exec…()funzioni. Ciò che fanno le shell include i precursori del meccanismo degli script eseguibili, ancora oggi presente in alcune shell (e anche oggi richiesto per il exec…p()sottoinsieme di funzioni), ed è in qualche modo fuorviante. Ciò che lo standard deve affrontare a questo proposito è il funzionamento di exec…()uno script interpretato e, al momento della creazione originale di POSIX , non funzionava in primo luogo su gran parte dello spettro dei sistemi operativi di destinazione .

Una domanda subordinata è perché questo non è stato standardizzato da allora, specialmente perché il meccanismo dei numeri magici per gli interpreti di script aveva raggiunto il pubblico nella parte AT&T dell'universo ed era stato documentato exec…()nella System 5 Interface Definition , all'inizio degli anni '90 :

Un file di interprete inizia con una riga del modulo

#! nome percorso [arg]
dove pathname è il percorso dell'interprete e arg è un argomento facoltativo. Quando si è execun file interprete, il sistema execè l'interprete specificato.
- exec. System V Interface Definition . Volume 1. 1991.

Sfortunatamente, il comportamento rimane oggi quasi altrettanto divergente rispetto agli anni '80 e non esiste un comportamento veramente comune da standardizzare. Alcuni Unices (notoriamente HP-UX e FreeBSD, per esempio) non supportano gli script come interpreti per gli script. Se la prima riga è uno, due o molti elementi separati da spazi bianchi varia tra MacOS (e versioni di FreeBSD prima del 2005) e altri. La lunghezza massima del percorso supportato varia. e i caratteri che non fanno parte del set di caratteri POSIX per i nomi di file portatili sono complicati, così come gli spazi bianchi iniziali e finali. Ciò che l'argomento 0 °, 1 ° e 2 ° finisce per finire è anche complicato, con variazioni significative tra i sistemi. Alcuni attualmente conformi a POSIX ma non-I sistemi Unix non supportano ancora alcun meccanismo di questo tipo e il loro mandato li convertirà in non più conformi a POSIX.

Ulteriori letture


1

Come notato da alcune delle altre risposte, le implementazioni variano. Ciò rende difficile standardizzare e preservare la retrocompatibilità con gli script esistenti. Questo vale anche per i moderni sistemi POSIX. Ad esempio, Linux non tokenizza completamente la linea shebang per spazi. macOS non consente all'interprete di script di essere un altro script.

Vedi anche http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

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.