什么是适合的Go shebang行?

huangapple go评论76阅读模式
英文:

What's the appropriate Go shebang line?

问题

#!/usr/bin/env go

英文:

I like using shebangs to run my Perl scripts directly:

#!/usr/bin/env perl

What's the shebang for Go programs?

答案1

得分: 49

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

示例:

//usr/bin/go run $0 $@ ; exit
package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

go将//视为单行注释,shell忽略额外的/

更新:Go安装可能在不同的位置。下面的语法将考虑到这一点,并适用于Mac:

//$GOROOT/bin/go run $0 $@ ; exit

英文:

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

example:

//usr/bin/go run $0 $@ ; exit
package main

import "fmt"

func main() {
	fmt.Println("Hello World!")
}

go treat // as a single line comment
and shell ignore extra /

Update: Go installation may be in a different location. The syntax below will take that into account, and works for Macs:

//$GOROOT/bin/go run $0 $@ ; exit

答案2

得分: 47

我更喜欢这个:

///usr/bin/true; exec /usr/bin/env go run "$0" "$@"

与هومن جاویدپور的答案相比,这有几个优点:

  • ///usr/bin/true 同时是有效的 Go 和 shell 语法。在 Go 中它是一个注释,在 shell 中它是一个空操作命令。

  • 使用 exec 替换新的 shell 进程,而不是启动一个孙子进程。因此,你的 Go 程序将成为一个直接的子进程。这更高效,对于一些高级情况(如调试和监控)也很重要。

  • 正确引用参数。空格和特殊字符不会引起问题。

  • 前导的 /// 比仅仅使用 // 更符合标准。如果只使用 //,你可能会遇到实现定义的行为。这是来自 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html 的引用:

> 如果一个路径名以两个连续的 / 字符开头,则在第一个 / 字符后的组件可能以实现定义的方式解释,尽管多于两个前导 / 字符应被视为一个单独的 / 字符。

我已经在 bash、dash、zsh 和 ksh 中测试了这个答案。

示例:

///usr/bin/true; exec /usr/bin/env go run "$0" "$@"
package main
import "fmt"
func main() {
    fmt.Println("你好!")
}
英文:

I prefer this:

///usr/bin/true; exec /usr/bin/env go run "$0" "$@"

This has several advantages compared to the answer by هومن جاویدپور:

  • The ///usr/bin/true accomplishes the feat of simultaneously being valid Go and shell syntax. In Go it is a comment. In shell, it is no-op command.

  • Uses exec to replace the new shell process instead of launching a grandchild process. As a result, your Go program will be a direct child process. This is more efficient and it's also important for some advanced situations, such as debugging and monitoring.

  • Proper quoting of arguments. Spaces and special characters won't cause problems.

  • The leading /// is more standards compliant than just //. If you only use //, you run the risk of bumping into implementation-defined behaviour. Here's a quote from http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html:

> If a pathname begins with two successive / characters, the first
> component following the leading / characters may be interpreted
> in an implementation-defined manner, although more than two leading
> / characters shall be treated as a single / character.

I have tested this answer with bash, dash, zsh, and ksh.

Example:

///usr/bin/true; exec /usr/bin/env go run "$0" "$@"
package main
import "fmt"
func main() {
    fmt.Println("你好!")
}

答案3

得分: 16

默认情况下没有这个功能。但是有一个名为gorun的第三方工具可以实现这个功能。https://wiki.ubuntu.com/gorun

不幸的是,编译器不支持shebang行。你不能用gorun运行的代码进行编译。

英文:

There isn't one by default. There is a third-party tool called gorun that will allow you to do it, though. https://wiki.ubuntu.com/gorun

Unfortunately the compilers don't like the shebang line. You can't compile the same code you run with gorun.

答案4

得分: 8

Go程序被编译为二进制文件;我不认为有直接从源代码运行它们的选项。

这与其他编译语言(如C++或Java)类似。一些语言(如Haskell)提供了完全编译模式和“脚本”模式,您可以使用shebang行直接从源代码运行它们。

英文:

Go programs are compiled to binaries; I don't think there is an option to run them directly from source.

This is similar to other compiled languages such as C++ or Java. Some languages (such as Haskell) offer both a fully compiled mode and a "script" mode which you can run directly from source with a shebang line.

答案5

得分: 1

在我的情况下,没有GOROOT环境变量。所以我做了这个。

//$(go env | grep -i goroot | awk -F= '{print $2}' | sed 's/\"//g')/bin/go run $0 $@ ; exit
英文:

In my case, there was no GOROOT env. So I did this.

//$(go env | grep -i goroot | awk -F= '{print $2}' | sed 's/\"//g')/bin/go run $0 $@ ; exit

答案6

得分: 0

到目前为止,将sh和gorun链接起来已经被证明是我最可移植的解决方案。

///bin/sh -c true && exec gorun "$0" "$@"

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	fmt.Println("hello")
	log.Printf("args: %v", os.Args)

	// just to test exit code, would be 0x11
	os.Exit(17)
}

输出:

00:~ $ chmod a+x test.go && ./test.go rawr
hello
2020/01/21 23:17:40 args: [./test.go rawr]
11:~ $ 
英文:

So far, chaining sh and gorun has proven to be the most portable solution for me.

///bin/sh -c true && exec gorun "$0" "$@"

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	fmt.Println("hello")
	log.Printf("args: %v", os.Args)

	// just to test exit code, would be 0x11
	os.Exit(17)
}

OUTPUT:

00:~ $ chmod a+x test.go && ./test.go rawr
hello
2020/01/21 23:17:40 args: [./test.go rawr]
11:~ $ 

答案7

得分: 0

/// 2>/dev/null; gorun "$0" "$@" ; exit $?

在我看来,这是迄今为止最好的答案组合。它使用gorun,因此可以缓存编译步骤(大大加快脚本的速度),而且在 macOS 上也可以使用。

使用以下命令安装gorun

go get github.com/erning/gorun

英文:

/// 2>/dev/null; gorun "$0" "$@" ; exit $?

Seems to me the best combination of the answers thus far. It uses gorun so it caches the compilation step (which speeds up your scripts greatly), and works on macOS, too.

Install gorun with:

go get github.com/erning/gorun

答案8

得分: 0

///bin/sh -c true && exec /usr/bin/env go run "$0" "$@"

英文:

Reading all this messages this seems the most portable:

///bin/sh -c true && exec /usr/bin/env go run "$0" "$@"

答案9

得分: 0

如果你真的想要一个可执行文件(不是用于shell的可调用文件),并且不想要依赖gorun,一个可能的解决方案可以是这样的:

#!/bin/sh
UMASK=`umask`
umask 077
tail -n +8 "$0" > /tmp/main.tmp.$$.go
umask $UMASK
(sleep 1 && rm -f /tmp/main.tmp.$$.go) &
exec go run /tmp/main.tmp.$$.go "$@"

package main

import "fmt"

func main() {
	fmt.Println("hi")
}
英文:

if really you want an executable (callable not fro shells) and not wanting to require gorun a possible solution could be something like:

#!/bin/sh
UMASK=`umask`
umask 077
tail -n +8 "$0" > /tmp/main.tmp.$$.go
umask $UMASK
(sleep 1 && rm -f /tmp/main.tmp.$$.go) &
exec go run /tmp/main.tmp.$$.go "$@"

package main

import "fmt"

func main() {
	fmt.Println("hi")
}

答案10

得分: 0

我的偏好是这样的:

```bash
///usr/bin/env go run "$0" "$@"; exit

你可以在我的个人hello_world.go文件中看到这个。如果我改变主意,我会更新那个文件。

这样做是两个答案的混合(这里这里)。

解释:

  1. 我使用3个斜杠,就像上面的第二个答案所说的那样,以避免实现定义的行为并更加符合规范。在bash中,多个斜杠被解释为一个斜杠,所以///usr/bin/env/usr/bin/env是一样的。而且,在Go中,双斜杠//是一个注释。所以,现在第一行既是一个有效的Bash/shell命令,又是一个有效的Bash注释。
  2. 使用/usr/bin/env go/usr/bin/go更好,因为你的go可执行文件可能不在/usr/bin/go位置。例如,which go显示我的位置在/usr/local/go/bin/goenv程序可以帮助找到正确的位置。
  3. 如果你的文件名是hello_world.go,那么直接运行./hello_world.go将调用go run "$0" "$@",这将在这个可执行文件上调用go run,将其路径作为$0传递,并将所有其他参数作为$@传递。
  4. 在最后调用exit确保bash在那一点退出文件,并避免尝试将你的Go代码作为shell代码运行。这就像在第一行之后将整个Go代码注释掉(至少对于你的shell来说)。

来自hello_world.go

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

package main

import "fmt"
import "os"

func main() {
    fmt.Println("Go: Hello World.")

    os.Exit(1)
}

示例运行命令和输出:

eRCaGuy_hello_world/go$ ./hello_world.go
Go: Hello World.
exit status 1

eRCaGuy_hello_world/go$ go build -o bin/ hello_world.go && bin/hello_world
Go: Hello World.

eRCaGuy_hello_world/go$ go run hello_world.go
Go: Hello World.
exit status 1

另请参阅

  1. 关于C和C++的答案,请参见这里:Run C or C++ file as a script

参考资料

  1. 这里的其他两个答案:
    1. https://stackoverflow.com/a/17900932/4561887
    2. https://stackoverflow.com/a/30082862/4561887
  2. 我最早看到关于//exit部分的很好解释的地方:Shebang starting with //?

<details>
<summary>英文:</summary>

My preference is this:

```bash
///usr/bin/env go run &quot;$0&quot; &quot;$@&quot;; exit

You can see this in my personal hello_world.go file. If I ever change my mind, I'll update that file.

Doing it this way is a bit of a mix between the main two answers right now (here and here).

Explanation:

  1. I use 3 slashes like the 2nd answer above says to avoid implementation-defined behavior and be more-compliant. In bash, the multiple slashes are interpreted as a single slash, so ///usr/bin/env is the same as /usr/bin/env. And, in Go, a double slash // is a comment. So, now the first line is both a valid Bash/shell command and a valid Bash comment.
  2. Using /usr/bin/env go is better than /usr/bin/go because your go executable may not be located at /usr/bin/go. which go for me shows mine is located at /usr/local/go/bin/go, for example. The env program helps find your proper location.
  3. If your file is called hello_world.go, then running it directly as ./hello_world.go will call go run &quot;$0&quot; &quot;$@&quot;, which calls go run on this executable, passing in the path to it as $0 and all other arguments as $@.
  4. Calling exit at the end ensures that bash exits the file at that point and avoids trying to run your Go code as shell code. It's like commenting out the whole Go (to your shell at least) after the first line.

From hello_world.go:

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

package main

import &quot;fmt&quot;
import &quot;os&quot;

func main() {
    fmt.Println(&quot;Go: Hello World.&quot;)

    os.Exit(1)
}

Sample run commands, and output:

eRCaGuy_hello_world/go$ ./hello_world.go
Go: Hello World.
exit status 1

eRCaGuy_hello_world/go$ go build -o bin/ hello_world.go &amp;&amp; bin/hello_world
Go: Hello World.

eRCaGuy_hello_world/go$ go run hello_world.go
Go: Hello World.
exit status 1

See also

  1. For my answer for C and C++, see here: Run C or C++ file as a script

References

  1. These other 2 answers here:
    1. https://stackoverflow.com/a/17900932/4561887
    2. https://stackoverflow.com/a/30082862/4561887
  2. Where I first saw a good explanation of the // and exit parts: Shebang starting with //?

huangapple
  • 本文由 发表于 2011年10月10日 07:06:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/7707178.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定