「//」で始まるシバン?


60

次のスクリプトについて混乱しています(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

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


//&>/dev/null;x="${0%.*}";[ ! "$x" -ot "$0" ]||(rm -f "$x";cc -o "$x" "$0")&&exec "$x" "$@" ...
州立モニカ

2
以下の@ g-manとJörgのコメントに従い、gillesの回答(unix.stackexchange.com/a/1919/27616)によれば、このトリックは最も互換性がある///....代わりに使用する必要があります//...
オリビエデュラック14年

1
この正しく(ディレクトリまたは場所)の引数を処理しませんより引用符なしのスペースを持つ:go run "$0" "$@"
チャールズ・ダフィー

回答:


71

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

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

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

しかし、なぜ、適切なシバンの//代わりに始まるのですか?/#!

これは、ファイルが有効なgoスクリプトである必要があるか、goが文句を言うためです。goでは、文字//はコメントを示すため、goは最初の行をコメントと見なし、解釈しようとしません。#ただし、この文字はコメントを示していないため、goがファイルを解釈するときに通常のシバンはエラーになります。

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


10
これはシェルではなくカーネルによって処理されます。Linuxが複数のパス区切り文字を処理する方法(/ home //// username /// file)に対するGillesの回答を参照してください
Gマン14年

3
@HermanTorjussen機能-パスのSYNAXはかなりよく定義されている、便利なバリエーションの多くを許可する-とパワーと複雑さを来る:/パスサフィックスは次のように定義されています/.。ときにaシンボリックリンクではない、aと同じであるa/と同じであるa/.セラは、パスを追加取得することができます例です/意味での変化がありません。正規のパスを導出する場合、連続するスラッシュを1つに縮小する正規化ステップがあります。確かに、それは正式な構文のきれいな部分ではありません。
フォルカーシーゲル14年

13
実際、POSIXでは、パスの最初に正確に2つのスラッシュがある場合を除き、複数のスラッシュは単一のスラッシュと同じであると述べています。ここにあるように。その場合、パスの解釈は実装に依存します。「パス名が2つの連続した<スラッシュ>文字で始まる場合、先頭の<スラッシュ>文字に続く最初のコンポーネントは、実装定義の方法で解釈されますが、 2つの先行<スラッシュ>文字は、単一の<スラッシュ>文字として扱われます。
ヨルグWミットタグ14年

11
だから、それがポータブルにするために1を代わりに書くべき///usr/bin/env go run $0 $@ ; exit...
ルスラン

1
@geekシェルは終了しますが、goインタープリターを起動する前は終了しません。Goはシェルではなく、Hello Worldを印刷しています。
ケーシー14

8

デフォルトでは、実行可能ファイルは/ 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スラッシュはパスで処理されるべき方法の詳細については。

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

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


9
「//はパスでは無視されます」-それは保証されません:「パス名が2つの連続した<slash>文字で始まる場合、先頭の<slash>文字に続く最初のコンポーネントは実装定義の方法で解釈される可能性があります」(pubs .opengroup.org / onlinepubs / 9699919799 / basedefs / ...
イェルクWミッターク

非常に興味深い、更新された回答。
gena2x 14年

1
...特にAFSは異なる方法で実装されますが、もはや一般的ではありません。
チャールズダフィー14年

0

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.