53

I like using shebangs to run my Perl scripts directly:

#!/usr/bin/env perl

What's the shebang for Go programs?

mcandre
  • 22,868
  • 20
  • 88
  • 147
  • 7
    Go is compiled. You shouldn't need a shebang. Right? – Austin Marshall Oct 09 '11 at 23:12
  • 1
    [Discussion thread in golang-nuts](https://groups.google.com/d/msg/golang-nuts/iGHWoUQFHjg/hEhYPQjP3HcJ) which have one post similar to [هومن جاویدپور](http://stackoverflow.com/users/1040891)'s [answer](http://stackoverflow.com/a/17900932/109747) – Afriza N. Arief Feb 17 '16 at 08:47
  • See also: [Unix & Linux: Shebang starting with `//`?](https://unix.stackexchange.com/q/162531/114401) – Gabriel Staples Feb 18 '23 at 06:26
  • You can also do this in C and C++: [Run C or C++ file as a script](https://stackoverflow.com/a/29709521/4561887) – Gabriel Staples Feb 18 '23 at 06:30

10 Answers10

49

//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

Murilo Perrone
  • 452
  • 4
  • 7
Houman Javidpour
  • 468
  • 8
  • 11
  • 2
    Could you please edit the shebang to apply to any Go file, not just hello_world.go? Maybe use `$0` or such? – mcandre Aug 05 '13 at 18:52
  • 9
    That's not a shebang line. It should start with `#!`. (Knowing that go doesn't like it). This is non portable, but perhaps good enough for many people, so +1 for the answer. – topskip Aug 21 '13 at 06:57
  • 4
    I think it's better to write like this: `///usr/bin/env go run $0 $@ ; exit` – ivanzoid Jul 11 '14 at 14:09
  • 18
    A slight improvement on @ivanzoid's version that I am using: `//usr/bin/env go run "$0" "$@"; exit "$?"` – KylePDavis Nov 09 '14 at 19:12
  • 1
    I can run this with `bash hello.go`, but not with just `./hello.go`. Does this work for somebody else? I'm on macOS, maybe this works differently on Linux? – Johan Walles Mar 21 '21 at 10:59
  • @JohanWalles, this happens also for Linux. `//` is not a shebang, it's something accepted as a comment in Go and a shell command by shells. That is `./hello.go` does not work. That's why `gorun` was created. It's a pity go does not ignore shebang lines, I don't think it would cost a lot of effort – Frediano Ziglio Jul 28 '21 at 17:15
  • go installed in a different location >> likewise you can `//usr/bin/env go run $0 $@ ; exit`. – Guy Rapaport Sep 19 '22 at 09:36
  • Cute trick. Note that a space after the double slash is convention for code comment style, among the wider C language family. That is, a style formatter is likely to introduce a space there, which may or may not break the interpreter path, depending on the shell implementation. – mcandre Feb 19 '23 at 16:33
48

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("你好!")
}
likebike
  • 1,167
  • 1
  • 11
  • 16
  • 2
    I want to point out that if your program os.Exit()s with a non-zero exit code that fact is printing to stderr and go run returns 1 as its exit code. – tmc Aug 25 '15 at 21:28
  • 1
    This is also true of the answer from هومن جاویدپور – Brad Peabody Nov 19 '15 at 20:13
  • 3
    Using `/bin/true` is not as portable (e.g. OSX and Zsh) – Kreisquadratur Dec 02 '15 at 11:34
  • 2
    I use `///bin/true; TMPDIR="$(cd "$(dirname "${0}")" && pwd)" exec /usr/bin/go run "$0" "$@"` for it to work when /tmp is mounted with the no exec flag – Javier López Jan 26 '17 at 18:00
  • 1
    This works well for me on current OS X and CentOS versions: `///usr/bin/true; exec /usr/bin/env go run "$0" "$@"`. What is /usr/bin/true doing at the front of this command though? – davejagoda Jan 30 '21 at 20:02
  • 3
    @davejagoda -- the `///usr/bin/true` is just a way to begin the line with '//' which is the Go comment syntax, while still being a valid shell command. The 'true' command effectively does nothing. The real action happens with 'exec'. The reason we can't just start off with 'exec' is because it is shell builtin, not a normal program. – likebike Feb 01 '21 at 06:46
  • 1
    I made a tool based on your suggestion at https://github.com/yugr/gcc-interp – yugr Jan 18 '22 at 00:20
  • 1
    quirks: 1) filename must have ".go" suffix 2) go fmt malforms this shebang by inserting space beetween second and third slash, forcing shell into trying to open nonexistent "//" program – gizlu Sep 21 '22 at 18:55
16

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.

Evan Shaw
  • 23,839
  • 7
  • 70
  • 61
8

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.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
1

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
Brian Choi
  • 66
  • 4
0

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:~ $ 
user3200607
  • 107
  • 1
  • 2
0

/// 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

HappyFace
  • 3,439
  • 2
  • 24
  • 43
0

Reading all this messages this seems the most portable:

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

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")
}
Frediano Ziglio
  • 310
  • 1
  • 6
0

My preference is this:

///usr/bin/env go run "$0" "$@"; 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 "$0" "$@", 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 "$0" "$@"; exit

package main

import "fmt"
import "os"

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

    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 && 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 //?
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265