
Estou confuso sobre seguir o script ( hello.go
).
//usr/bin/env go run $0 $@ ; exit
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
Ele pode ser executado. (no MacOS X 10.9.5)
$ chmod +x hello.go
$ ./hello.go
hello, world
Eu não ouvi falar de shebang começando com //
. E ainda funciona quando insiro uma linha em branco no topo do script. Por que esse script funciona?
Responder1
Não é uma coisa simples, é apenas um script que é executado pelo shell padrão. O shell executa a primeira linha
//usr/bin/env go run $0 $@ ; exit
o que faz com go
que seja invocado com o nome deste arquivo, então o resultado é que este arquivo é executado como um script go e então o shell sai sem olhar o resto do arquivo.
Mas por que começar com, //
em vez de apenas /
ou com uma coisa adequada #!
?
Isso ocorre porque o arquivo precisa ser um script go válido, ou o go reclamará. Em go, os caracteres //
denotam um comentário, então go vê a primeira linha como um comentário e não tenta interpretá-la. O caractere #
, entretanto, não denota um comentário, portanto, um shebang normal resultaria em um erro ao interpretar o arquivo.
Esse motivo para a sintaxe é apenas construir um arquivo que seja um script shell e um script go, sem que um pise no outro.
Responder2
Ele é executado porque, por padrão, o arquivo executável é considerado o script /bin/sh. Ou seja, se você não especificou nenhum shell específico - é #!/bin/sh.
O // é simplesmente ignorado nos caminhos - você pode considerar que é um único '/'.
Então você pode considerar que possui um script de shell com a primeira linha:
/usr/bin/env go run $0 $@ ; exit
O que essa linha faz? Ele executa 'env' com parâmetros 'go run $0 $@'. lá 'go' é o comando e 'run $0 $@' são argumentos e sai do script depois. $0 é o nome deste script. $@ são argumentos originais do script. Então esta linha é executada go, que executa este script com seus argumentos
Há detalhes bastante interessantes, conforme apontado nos comentários, de que duas barras são definidas pela implementação, e esse script se tornaria correto para POSIX se especificasse três ou mais barras. Referir-sehttp://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.htmlpara obter detalhes sobre como as barras devem ser tratadas nos caminhos.
Observe também que há outro erro no script $@, é correto usar "$@", caso contrário, se algum parâmetro contiver espaços, ele será dividido em vários parâmetros. Por exemplo você não pode passar o nome do arquivo com espaços se não estiver usando o "$@"
Este script em particular obviamente depende da ideia de que '//' é igual a '/'
Responder3
Isso funcionará para C++ (e C se esse C permitir // comentários)
//usr/bin/env sh -c 'p=$(expr '"_$0"' : "_\(.*\)\.[^.]*"); make $p > /dev/null && $p'; exit