Shebang começando com `//`?

60

Estou confuso sobre o seguinte script ( hello.go).

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

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

Pode executar. (no MacOS X 10.9.5)

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

Eu não ouvi sobre shebang começando com //. E ainda funciona quando insiro uma linha em branco na parte superior do script. Por que esse script funciona?

kawty
fonte
//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
Reinstaate Monica #
2
seguindo os comentários de g-man e Jörg abaixo e de acordo com a resposta de gilles ( unix.stackexchange.com/a/1919/27616 ), esse truque deve ser usado em ///....vez de //...ser o mais compatível!
Olivier Dulac
1
Isso não vai lidar corretamente com argumentos (ou local em um diretório) com espaços sem mais citações:go run "$0" "$@"
Charles Duffy

Respostas:

71

Não é um shebang, é apenas um script que é executado pelo shell padrão. O shell executa a primeira linha

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

que faz gocom que seja chamado com o nome desse arquivo, portanto, o resultado é que esse arquivo é executado como um script go e, em seguida, o shell sai sem olhar para o restante do arquivo.

Mas por que começar em //vez de apenas /ou de uma boa transa #!?

Isso ocorre porque o arquivo precisa ser um script go válido ou o go vai reclamar. Em movimento, os caracteres //denotam um comentário; portanto, ele vê a primeira linha como um comentário e não tenta interpretá-lo. O caractere #, no entanto, não indica um comentário; portanto, um shebang normal resultaria em um erro quando o interpretar fosse o arquivo.

Esse motivo para a sintaxe é apenas para criar um arquivo que seja um script de shell e um script de ir sem que um pisar no outro.

Casey
fonte
10
É tratado pelo kernel, não pelo shell; veja a resposta de Gilles para Como o Linux lida com vários separadores de caminho (/ home //// username /// file) .
G-Man diz 'Reinstate Monica'
3
Recurso @HermanTorjussen - a sinaxia de caminhos está bem definida, permitindo muitas variantes úteis - e com poder vem a complexidade: /como o sufixo do caminho é definido /.; Quando anão é um link simbólico, aé o mesmo a/que a/.Thera. São casos em que um caminho pode obter um adicional /sem alteração de significado. Ao derivar um caminho canônico, há uma etapa de normalização contratando barras consecutivas para um. É verdade que não é uma parte limpa da sintaxe formal.
Volker Siegel
13
Na verdade, o POSIX diz que várias barras são iguais a uma única barra, exceto quando há exatamente duas barras exatamente no início do caminho. Como é o caso aqui. Nesse caso, a interpretação do caminho depende da implementação: "Se um nome de caminho começar com dois caracteres sucessivos <slash>, o primeiro componente após os caracteres iniciais <slash> poderá ser interpretado de uma maneira definida pela implementação, embora mais de dois caracteres <slash> iniciais devem ser tratados como um único caractere <slash> ".
Jörg W Mittag 17/10
11
Então, para torná-lo portátil deve-se, em vez escrever ///usr/bin/env go run $0 $@ ; exit...
Ruslan
1
@geek o shell sai, mas não antes de iniciar o interpretador go. Go está imprimindo olá mundo, não a casca.
precisa
8

Ele é executado porque, por padrão, o arquivo executável é considerado como / bin / sh script. Ou seja, se você não especificou nenhum shell em particular - é #! / Bin / sh.

O // é apenas ignorado nos caminhos - você pode considerar que está no único '/'.

Portanto, você pode considerar que possui um shell script com a primeira linha:

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

O que essa linha faz? Ele roda 'env' com os parâmetros 'go run $ 0 $ @'. lá 'go' é o comando e 'run $ 0 $ @' são argumentos e sai do script posteriormente. $ 0 é esse nome de script. $ @ são argumentos de script originais. Portanto, esta linha é executada e executa esse script com seus argumentos

Existem detalhes bastante interessantes, como apontado nos comentários, de que duas barras são definidas pela implementação e esse script se tornaria POSIX correto se especificar três ou mais barras. Consulte http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html para obter detalhes sobre como as barras devem ser tratadas nos caminhos.

Observe também que há outro erro no script: $ @ é correto usar "$ @", porque, caso contrário, se algum parâmetro contiver espaços, ele será dividido em muitos parâmetros. Por exemplo, você não pode passar o nome do arquivo com espaços se não estiver usando o "$ @"

Esse script em particular obviamente depende da ideia de que '//' é igual a '/'

gena2x
fonte
9
"O // é apenas ignorado nos caminhos" - Isso não é garantido: "Se um nome de caminho começar com dois caracteres sucessivos <slash>, o primeiro componente após os caracteres iniciais <slash> poderá ser interpretado de uma maneira definida pela implementação" ( pubs .opengroup.org / onlinepubs / 9699919799 / basedefs /… )
Jörg W Mittag
Resposta muito interessante e atualizada.
gena2x
1
... o AFS em particular implementou // de forma diferente, mas não é mais comum.
Charles Duffy
0

Isso funcionará para C ++ (e C, se esse C permitir // para comentários)

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

Matthew Hannigan
fonte