Shebang commençant par `//`?

60

Je suis confus de suivre script ( hello.go).

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

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

Il peut exécuter. (sur MacOS X 10.9.5)

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

Je n'ai pas entendu parler de shebang à partir de //. Et cela fonctionne toujours lorsque j'insère une ligne vierge en haut du script. Pourquoi ce script fonctionne-t-il?

kawty
la source
//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
REINSTATE MONICA - Jeremy Banks
2
Après les commentaires de @ g-man et Jörg ci-dessous, et selon la réponse de gilles ( unix.stackexchange.com/a/1919/27616 ), cette astuce devrait utiliser ///....au lieu d' //...être la plus compatible!
Olivier Dulac
1
Cela ne gérera pas correctement les arguments (ou l'emplacement dans un répertoire) avec des espaces sans plus de guillemets:go run "$0" "$@"
Charles Duffy

Réponses:

71

Ce n'est pas un shebang, c'est juste un script qui est exécuté par le shell par défaut. Le shell exécute la première ligne

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

ce qui a gopour effet d'appeler le nom de ce fichier. Il en résulte que ce fichier est exécuté en tant que script go puis que le shell se ferme sans regarder le reste du fichier.

Mais pourquoi commencer par //au lieu de juste /ou d'un shebang approprié #!?

Cela est dû au fait que le fichier doit être un script valide, sinon Go se plaindra. Dans go, les caractères //désignent un commentaire, alors go voit la première ligne comme un commentaire et ne tente pas de l'interpréter. Le caractère #cependant, ne représente pas un commentaire, donc une tralala normale entraînerait une erreur lors de passer interprète le fichier.

Cette raison de la syntaxe est simplement de construire un fichier qui est à la fois un script shell et un script aller sans que l'un ne se place sur l'autre.

casey
la source
10
C'est géré par le noyau, pas par le shell; voir la réponse de Gilles à Comment Linux gère plusieurs séparateurs de chemins (/ home //// nom_utilisateur /// fichier) .
G-Man dit 'Réintégrez Monica'
3
@HermanTorjussen Feature - la synaxe des chemins est assez bien définie, ce qui permet de nombreuses variantes utiles - et avec la puissance vient la complexité: /comme suffixe de chemin est défini comme /.; Quand an'est pas un lien symbolique, aest identique à a/ce qui est identique à a/.Thera sont des cas où un chemin peut en obtenir un supplémentaire /sans changement de sens. Lorsque vous dérivez un chemin canonique, il existe une étape de normalisation qui réduit les barres obliques consécutives à une barre oblique. Certes, cela ne fait cependant pas partie de la syntaxe formelle.
Volker Siegel
13
En fait, POSIX dit que plusieurs barres obliques sont identiques à une seule barre oblique, sauf quand il y a exactement deux barres obliques exactement au tout début du chemin. Comme c'est le cas ici. Dans ce cas, l’interprétation du chemin dépend de la mise en oeuvre: "Si un chemin commence par deux caractères <slash> successifs, le premier composant suivant les caractères <slash> qui précèdent peut être interprété de manière définie, bien que plus de deux caractères <slash> en tête doivent être traités comme un seul caractère <slash>. "
Jörg W Mittag
11
Donc, pour le rendre portable, on devrait plutôt écrire ///usr/bin/env go run $0 $@ ; exit...
Ruslan
1
@geek le shell se ferme mais pas avant de lancer l'interprète go. Go imprime bonjour le monde, pas la coquille.
Casey
8

Il s'exécute car, par défaut, le fichier exécutable est supposé être le script / bin / sh. C'est-à-dire si vous n'avez pas spécifié de shell particulier - c'est #! / Bin / sh.

Le // est simplement ignoré dans les chemins - vous pouvez le considérer comme un simple '/'.

Vous pouvez donc considérer que vous avez un script shell avec la première ligne:

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

Que fait cette ligne? Il fonctionne 'env' avec les paramenters 'go run $ 0 $ @'. il y a 'go' est command et 'run $ 0 $ @' sont des arguments et des scripts de sortie par la suite. $ 0 est ce nom de script. $ @ sont des arguments de script originaux. Donc, cette ligne lance go qui exécute ce script avec ses arguments

Comme indiqué dans les commentaires, il existe des détails assez intéressants selon lesquels deux barres obliques sont définies par l'implémentation, et ce script deviendrait POSIX-correct s'il spécifiait trois barres obliques ou plus. Reportez-vous à http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html pour plus de détails sur la manière dont les barres obliques doivent être traitées dans les chemins.

Notez également qu'il y a une autre erreur dans le script $ @ il est correct d'utiliser "$ @" à la place, car sinon, si un paramètre contient des espaces, il sera divisé en plusieurs paramètres. Par exemple, vous ne pouvez pas transmettre le nom de fichier avec des espaces si vous n'utilisez pas le "$ @"

Ce script particulier repose évidemment sur l'idée que '//' est égal à '/'

Gena2x
la source
9
"Le // est simplement ignoré dans les chemins" - Cela n'est pas garanti: "Si un chemin d'accès commence par deux caractères <slash> successifs, le premier composant suivant les caractères <slash> qui précèdent peut être interprété de manière définie par l'implémentation" ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Jörg W Mittag
Très intéressant, réponse mise à jour.
gena2x
1
... AFS en particulier implémenté // différemment, mais ce n'est plus courant.
Charles Duffy
0

Cela fonctionnera pour C ++ (et C si ce C autorise // pour les commentaires)

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

Matthew Hannigan
la source