web-dev-qa-db-ja.com

`//`で始まるシバン?

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

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

61
kawty

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

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

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

しかし、なぜ//だけではなく/または適切なシバン#!

これは、ファイルが有効なgoスクリプトである必要があるためです。そうしないと、goが文句を言います。外出先では、文字//はコメントを表すので、goは最初の行をコメントとして認識し、解釈を試みません。キャラクター #ただし、コメントを示すものではないため、goがファイルを解釈すると、通常のシバンはエラーになります。

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

72
casey

デフォルトでは、実行可能ファイルは/ 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 を参照してください。

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

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

8
gena2x

これはC++(およびCがコメントに//を許可している場合はC)で機能します。

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

0