Shebang che inizia con `//`?


60

Sono confuso riguardo al seguente script ( hello.go).

//usr/bin/env go run $0 $@ ; exit

package main
import "fmt"
func main() {
    fmt.Printf("hello, world\n")
}

Può eseguire. (su MacOS X 10.9.5)

$ chmod +x hello.go
$ ./hello.go
hello, world

Non ho sentito parlare di Shebang a partire da //. E funziona ancora quando inserisco una riga vuota nella parte superiore dello script. Perché funziona questo script?


//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
— REINSTATE MONICA -Jeremy Banks,

2
seguendo i commenti di @ g-man e Jörg di seguito e in base alla risposta di Gilles ( unix.stackexchange.com/a/1919/27616 ), questo trucco dovrebbe essere usato ///....invece di //...essere il più compatibile!
— Olivier Dulac,

1
Questo non gestirà correttamente gli argomenti (o la posizione in una directory) con spazi senza più virgolette:go run "$0" "$@"
— Charles Duffy,

Risposte:


71

Non è uno shebang, è solo uno script che viene eseguito dalla shell predefinita. La shell esegue la prima riga

//usr/bin/env go run $0 $@ ; exit 

che provoca gol'invocazione con il nome di questo file, quindi il risultato è che questo file viene eseguito come uno script go e quindi la shell esce senza guardare il resto del file.

Ma perché iniziare //invece di un semplice /o giusto shebang #!?

Questo perché il file deve essere uno script go valido, altrimenti go si lamenterà. In go, i personaggi //indicano un commento, quindi go vede la prima riga come un commento e non tenta di interpretarlo. Il personaggio #tuttavia non denota un commento, quindi un normale shebang comporterebbe un errore quando go interpreta il file.

Questo motivo della sintassi è solo quello di creare un file che è sia uno script di shell che uno script di go senza passare da uno all'altro.



3
Funzione @HermanTorjussen - la sinossi dei percorsi è abbastanza ben definita, consentendo molte varianti utili - e con il potere arriva la complessità: /come il suffisso del percorso è definito come /.; Quando anon è un collegamento simbolico, aè lo stesso a/che è lo stesso di a/.Thera nei casi in cui un percorso può ottenere un ulteriore /senza alcun cambiamento di significato. Quando si ricava un percorso canonico, c'è una fase di normalizzazione che contrae barre consecutive a una. Certo, non è una parte pulita della sintassi formale, però.
— Volker Siegel,

13
In realtà, POSIX afferma che più barre sono le stesse di una singola barra tranne quando ci sono esattamente due barre esattamente all'inizio del percorso. Come è il caso qui. In tal caso, l'interpretazione del percorso dipende dall'implementazione: "Se un percorso inizia con due caratteri <slash> successivi, il primo componente che segue i caratteri <slash> iniziali può essere interpretato in modo definito dall'implementazione, sebbene più di due caratteri <slash> iniziali devono essere trattati come un singolo carattere <slash>. "
— Jörg W Mittag,

11
Quindi, per renderlo portatile si dovrebbe invece scrivere ///usr/bin/env go run $0 $@ ; exit...
— Ruslan,

1
@geek la shell esce ma non prima di lanciare l'interprete go. Go sta stampando ciao mondo, non la shell.
— Casey,

8

Funziona perché, per impostazione predefinita, si presuppone che il file eseguibile sia / bin / sh script. Vale a dire se non hai specificato alcuna shell particolare - è #! / Bin / sh.

// è appena ignorato nei percorsi - puoi considerarlo come '/' singolo.

Quindi puoi considerare di avere uno script di shell con la prima riga:

/usr/bin/env go run $0 $@ ; exit

Cosa fa questa linea? Funziona 'env' con i paramenters 'go run $ 0 $ @'. c'è 'go' è il comando e 'run $ 0 $ @' sono args ed esce successivamente dallo script. $ 0 è questo nome di script. $ @ sono argomenti di script originali. Quindi questa linea va avanti che esegue questo script con i suoi argomenti

Vi sono dettagli piuttosto interessanti, come sottolineato nei commenti, che due barre sono definite dall'implementazione e questo script diventerebbe POSIX corretto se specificasse tre o più barre. Fare riferimento a http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html per i dettagli su come gestire le barre nei percorsi.

Nota anche che c'è un altro errore nello script $ @ è corretto usare invece "$ @", perché altrimenti se un parametro contiene spazi verrà diviso in molti parametri. Ad esempio, non è possibile passare il nome del file con spazi se non si utilizza "$ @"

Questo particolare script ovviamente si basa sull'idea che "//" è uguale a "/"


9
"// è semplicemente ignorato nei percorsi" - Non è garantito: "Se un percorso inizia con due caratteri <slash> successivi, il primo componente che segue i caratteri <slash> iniziali può essere interpretato in modo definito dall'implementazione" ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
— Jörg W Mittag,

Risposta molto interessante e aggiornata.
— gena2x,

1
... AFS in particolare implementato // in modo diverso, ma non è più comune.
— Charles Duffy,

0

Funzionerà con C ++ (e C se quella C consente // per i commenti)

//usr/bin/env sh -c 'p=$(expr '"_$0"' : "_\(.*\)\.[^.]*"); make $p > /dev/null && $p'; exit

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.