次のスクリプト ( 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