次のスクリプト(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
//
で始まるシバンについて聞いたことがありません。また、スクリプトの上部に空白行を挿入しても機能します。なぜこのスクリプトが機能するのですか?
これはシバンではなく、デフォルトのシェルによって実行されるスクリプトにすぎません。シェルは最初の行を実行します
//usr/bin/env go run $0 $@ ; exit
これにより、このファイルの名前でgo
が呼び出されるため、結果として、このファイルはgoスクリプトとして実行され、シェルはファイルの残りの部分を調べずに終了します。
しかし、なぜ//
だけではなく/
または適切なシバン#!
?
これは、ファイルが有効なgoスクリプトである必要があるためです。そうしないと、goが文句を言います。外出先では、文字//
はコメントを表すので、goは最初の行をコメントとして認識し、解釈を試みません。キャラクター #
ただし、コメントを示すものではないため、goがファイルを解釈すると、通常のシバンはエラーになります。
構文のこの理由は、一方が他方を踏まずに、シェルスクリプトとgoスクリプトの両方であるファイルを構築するためです。
デフォルトでは、実行可能ファイルは/ bin/shスクリプトであると想定されているため、実行されます。つまり特定のシェルを指定しなかった場合-#!/ bin/shです。
//はパスでは無視されます-単一の '/'と見なすことができます。
したがって、最初の行にシェルスクリプトがあると考えることができます。
/usr/bin/env go run $0 $@ ; exit
この行は何をしますか? 'env'をパラメーター付きで実行し、 'go run $ 0 $ @'を実行します。 「go」はコマンド、「run $ 0 $ @」は引数であり、後でスクリプトを終了します。 $ 0はこのスクリプト名です。 $ @は元のスクリプト引数です。したがって、この行は実行され、引数を使用してこのスクリプトを実行します
コメントで指摘されているように、2つのスラッシュは実装定義であり、3つ以上のスラッシュを指定すると、このスクリプトはPOSIXで正しくなるという非常に興味深い詳細があります。パスでのスラッシュの処理方法の詳細については、 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html を参照してください。
また、スクリプト$$ @には別の間違いがあることに注意してください。代わりに "$ @"を使用するのが正しいです。それ以外の場合、パラメーターにスペースが含まれていると、多くのパラメーターに分割されます。たとえば、「$ @」を使用しない場合、スペースを含むファイル名を渡すことはできません
この特定のスクリプトは、「//」が「/」に等しいという考えに明らかに依存しています
これはC++(およびCがコメントに//を許可している場合はC)で機能します。
//usr/bin/env sh -c 'p=$(expr '"_$0"' : "_\(.*\)\.[^.]*"); make $p > /dev/null && $p'; exit