Go のバイナリを Perl スクリプトとしても扱う
環境は macOS 10.14.4 で go1.12.5 です。
まずは次のコードを読んでみましょう
package main import ( "fmt" "io/ioutil" ) const script = ` #!perl print "Hello, Perl World!!"; __END__ ` func init() { ioutil.Discard.Write([]byte(script)) } func main() { fmt.Println("This is Go world!!") }
これをビルドして実行してみます
go build -o main main.go
こうすると main
という実行用のバイナリができますね。
通常通り実行してみましょう。
$ ./main This is Go world!!
今度は Perl で実行してみましょう
$ perl -x ./main
Hello, Perl World!!
ワオ!!
解説
Go のコードに記載していた文字列が使われる場合、その文字列はバイナリに含まれるようです。(将来的にコンパイラがもっと賢くなれば、こういったことはなくなるかもしれません)
ビルドした Go のバイナリを覗いてみましょう
$ strings ./main | less # /perl で検索 #!perl print "Hello, Perl World!!"; __END__ exitsyscall: ... # 何かしら記述が続く
といったような感じになってます。 上記のバイナリに含まれる文字列を Perl のコードとして見てみるとこんな感じになります。
#!perl print "Hello, Perl World!!"; __END__ exitsyscall: ... # 何かしら記述が続く
ここまで分かれば今度は Perl 側の話に移ります。
Perl のコードで __END__
以下に何を記述されても全て意味を持たないものとして扱われるので無視されます。そしてもう一つ perl -x
の x オプションは #!perl
の行までのテキストを全て無視するようになります。
-x[directory] ignore text before #!perl line (optionally cd to directory)
これらの条件が全て揃うことによって実行可能なバイナリを Perl スクリプトとしても扱うことが可能になるわけです。
Ruby でも同じ -x
オプションがあるとのことなので是非試してみたいですね!!
協力してくださった @tompng さんありがとうございました!