64

I'm confused about following script (hello.go).

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

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

It can execute. (on MacOS X 10.9.5)

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

I haven't heard about shebang starting with //. And it still working when I insert a blank line at the top of the script. Why does this script work?

4

3 Answers 3

74

It isn't a shebang, it is just a script that gets run by the default shell. The shell executes the first line

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

which causes go to be invoked with the name of this file, so the result is that this file is run as a go script and then the shell exits without looking at the rest of the file.

But why start with // instead of just / or a proper shebang #! ?

This is because the file need to be a valid go script, or go will complain. In go, the characters // denote a comment, so go sees the first line as a comment and does not attempt to interpret it. The character # however, does not denote a comment, so a normal shebang would result in an error when go interprets the file.

This reason for the syntax is just to build a file that is both a shell script and a go script without one stepping on the other.

8
  • 10
    It's handled by the kernel, not the shell; see Gilles's answer to How Linux handles multiple path separators (/home////username///file). Oct 16, 2014 at 16:18
  • 3
    @HermanTorjussen Feature - the synax of paths is pretty well defined, allowing lots of useful variants - and with power comes complexity: / as path suffix is defined as /.; When a is not a symlink, a is the same as a/ which is the same as a/. Thera are cases where a path can get an additional / with no change in meaning. When deriving a canonical path, there is a normalisation step contracting consecutive slashes to one. Granted, it's not a clean part of the formal syntax, though. Oct 16, 2014 at 16:56
  • 13
    Actually, POSIX says that multiple slashes are the same as a single slash except when there are exactly two slashes exactly at the very beginning of the path. As is the case here. In that case, the interpretation of the path is implementation-dependent: "If a pathname begins with two successive <slash> characters, the first component following the leading <slash> characters may be interpreted in an implementation-defined manner, although more than two leading <slash> characters shall be treated as a single <slash> character." Oct 17, 2014 at 4:20
  • 12
    So, to make it portable one should instead write ///usr/bin/env go run $0 $@ ; exit...
    – Ruslan
    Oct 17, 2014 at 6:56
  • 1
    @geek the shell exits but not before launching the go interpreter. Go is printing hello world, not the shell.
    – casey
    Dec 18, 2014 at 12:42
8

It runs because by default executable file is assumed to be /bin/sh script. I.e. if you didn't specify any particular shell - it is #!/bin/sh.

The // is just ignored in paths - you can consider is at as single '/'.

So you can consider that you have shell script with first line:

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

What does this line do? It runs 'env' with paramenters 'go run $0 $@'. there 'go' is command and 'run $0 $@' are args and exits script afterwards. $0 is this script name. $@ are original script arguments. So this line runs go which runs this script with it's arguments

There are quite interesting details, as pointed in comments, that two slashes are implementation-defined, and this script would become POSIX-correct if it specify three or more slashes. Refer to http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html for details on how slashes should be handled in paths.

Note also that there is another mistake in script the $@ it's correct to use "$@" instead, because otherwise if any parameter contains spaces it will be split to many parameters. For example you can't pass file name with spaces if you not using the "$@"

This particular script obviously rely on the idea that '//' is equal to '/'

3
  • 9
    "The // is just ignored in paths" – That's not guaranteed: "If a pathname begins with two successive <slash> characters, the first component following the leading <slash> characters may be interpreted in an implementation-defined manner" (pubs.opengroup.org/onlinepubs/9699919799/basedefs/…) Oct 17, 2014 at 4:24
  • Very interesting, updated answer.
    – gena2x
    Oct 17, 2014 at 9:33
  • 1
    ...AFS in particular implemented // differently, but it's not common anymore. Oct 18, 2014 at 19:13
0

This will work for C++ (and C if that C allows // for comments)

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .