`//` で始まるシェバンですか?

`//` で始まるシェバンですか?

次のスクリプト ( hello.go) について混乱しています。

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

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

実行できます。(MacOS X 10.9.5の場合)

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

で始まるシェバンについては聞いたことがありません//。スクリプトの先頭に空白行を挿入しても、まだ機能します。なぜこのスクリプトは機能するのでしょうか?

答え1

これはシバンではなく、デフォルトのシェルによって実行される単なるスクリプトです。シェルは最初の行を実行します。

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

これgoにより、このファイルの名前でが呼び出され、結果としてこのファイルは go スクリプトとして実行され、その後、シェルはファイルの残りの部分を参照せずに終了します。

しかし、なぜ//ではなく/、 または適切な shebangから始めるのでしょうか#!?

これは、ファイルが有効な go スクリプトである必要があるためです。そうでない場合、go はエラーを出します//。go では、文字はコメントを表すため、go は最初の行をコメントとして認識し、解釈しようとしません。#ただし、文字はコメントを表すものではないため、通常のシェバンでは、go がファイルを解釈するときにエラーが発生します。

この構文の理由は、シェル スクリプトと Go スクリプトの両方であるファイルを、一方が他方を踏むことなく構築するためです。

答え2

デフォルトでは実行可能ファイルは /bin/sh スクリプトであると想定されるため、実行されます。つまり、特定のシェルを指定しなかった場合は #!/bin/sh になります。

// はパス内で無視されます。単一の '/' として考えることができます。

したがって、最初の行に次のシェル スクリプトがあると考えることができます。

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

この行は何をしますか? 'env' をパラメータ 'go run $0 $@' で実行します。'go' はコマンドで、'run $0 $@' は引数で、その後スクリプトを終了します。$0 はこのスクリプト名です。$@ は元のスクリプト引数です。つまり、この行は go を実行し、このスクリプトを引数で実行します。

コメントで指摘されているように、2つのスラッシュは実装定義であり、このスクリプトは3つ以上のスラッシュを指定するとPOSIXに準拠することになるという非常に興味深い詳細があります。http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.htmlパス内でスラッシュをどのように処理するかについての詳細。

また、スクリプトの $@ には別の間違いがあることにも注意してください。代わりに "$@" を使用するのが正しいです。そうしないと、パラメータにスペースが含まれている場合、複数のパラメータに分割されてしまいます。たとえば、"$@" を使用しない場合、スペースを含むファイル名を渡すことはできません。

この特定のスクリプトは明らかに「//」が「/」に等しいという考えに依存しています。

答え3

これは C++ でも機能します (C で // のコメントが許可されている場合は C でも機能します)

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

関連情報