ハイパフォーマンスなネットワークシステムや並列システムを開発するプログラム言語として、Goの採用が拡大していることから、開発者たちはこれをスクリプト言語として使用することに熱意を持ち始めている。ただし現在のGoは、そのままではbashやpythonの代用としては使えないため、これには多少の努力が必要だ。
CodelangのElton Minetto氏が説明しているように、そのパワーや簡潔さ、goroutineのサポートなど、スクリプト言語として使用する上でのGoには多くのアピールポイントがある。GoogleのソフトウェアエンジニアであるEyal Posener氏は、Goをスクリプト言語として使用するさらなる理由として、豊富なライブラリが使用可能であることと、言語自体が簡潔であるためスクリプトのメンテナンスが容易であることを挙げている。やや違う見方として、Goのコントリビュータで元GoogleのDavid Crawshaw氏は、普段は複雑なプログラムをGoで記述しているプログラマにとって、スクリプト処理にGoを使うことが便利である点に注目している。
私はいつもGoを書いているのですが、たまにbashやperl、pythonを書きます。たまになので、すぐに忘れてしまうのです。
毎日のタスクと、それほど頻繁ではないスクリプティングのタスクに同じ言語が使えれば、効率はずっとよくなる、というのが氏の主張だ。効率という点で言えば、Goは強い型付けを持った言語でもある。CloudflareのエンジニアであるIgnat Korchagin氏が言うように、これによってGoのスクリプトは信頼性が高く、タイプミスのような些細なエラーによってランタイムエラーが発生する可能性が低いのだ。
Codenationでは、開発ワークフローとCI/CDパイプラインの両方で、反復的なタスクを自動化するスクリプトの作成にGoを使用していた。Codenationでは、Goプログラムのコンパイルと実行をワンステップで行うGoツールチェーンのデフォルトツールを使用して、go run
によってGoスクリプトを実行している。go run
は実際にはインタプリタではない。Posener氏が記している。
[...] bashとpythonはインタプリタなので、スクリプトを読み込みながら実行します。これに対して、go runを入力した場合、Goは、Goプログラムをコンパイルした上で実行します。Goのコンパイル時間が極めて短いので、インタープリタのように見えるのです。
Goスクリプトをシェルスクリプトと同じように実行可能にするため、Codenationのエンジニアたちは、便利なGoパッケージをいくつも使用した。例えば、
-
Goの出力をカラーにするgithub.com/faith/color。
-
時間の掛かるオペレーション時にプログレスバーを表示するgithub.com/schollz/progressbar。
-
使用されているファイル名、行番号、関数などの情報をキャプチャするgithub.com/jimlawless/whereami。これはエラーメッセージの改善に利用できる。
-
入力処理、オプション、関連ドキュメントなどの複雑なスクリプトが簡単に作成できるgithub.com/spf13/cobra。
Codenationでは、コマンドラインからgo run
を使ってGoプログラムを実行する方法でうまくいっている、しかし、完璧なソリューションと言うには程遠い、とCrawshaw氏は書いている。特に、GoにはREPL(read-eval-print loop)がないため、スクリプトをバイナリプログラムのように実行可能にするシバン(shebang、"#!"で始まるスクリプト)として組み込むことができない。さらに、Goのエラー処理は、短いスクリプトよりも大規模なプログラムの方に適している。これらすべての理由から、氏は、ここに挙げたような制限をすべて解決するGoクローンの開発を目標としたプロジェクトを、Neugramという名称で立ち上げた。しかし残念ながら、おそらくはGoの構文を詳細に再現することの難しさから、Neugramは放棄された状態にあるようだ。
Neugramと同じようなアプローチを採用したのがgomacro
である。gomacroはGoのインタプリタで、コード生成と一部形式のジェネリクスを実装する手段としてLispライクなマクロをサポートする。
gomacroはすべてGoで実装された、ほぼ完全なGoインタプリタです。インタラクティブなREPLとスクリプティングモードの両方をサポートし、実行時にGoツールチェーンを必要としません(非常に特殊なケースとして、サードパーティパッケージを実行時にインポートする場合のみ例外です)。
スクリプティングに適したスイートであると同時に、gomacro
は、標準的なGoに転換される詳細様を表現する中間言語としてのGoの使用を可能にし、さらにはGoのソースコードデバッガとしても機能する。
gomacro
はスクリプティングにGoを使用する上で必要なフレキシビリティの大部分を提供するが、残念ながら標準Goではないため、さまざまな問題が発生する。Posener氏は、標準Goをスクリプト言語として使用する可能性についての詳細な分析を実施した。シバンの欠如に対する回避策も含まれる。しかしながら、どのアプローチにも一長一短がある。
どうやら、完璧なソリューションは存在しないようです。その理由はよく分かりません。Goスクリプトを実行するための、最も簡単で、最も問題の少ない方法は、go run コマンドを使用することです。[...] Go言語のこの領域では、まだ行うべきことが残っていると思います。シバン行を無視するように言語を変更しても、害は何もないはずです。
しかしLinuxシステムであれば、シバンを完全にサポートしたGoスクリプトをコマンドラインから実行可能にする、高度なトリックが使用できるかも知れない。Korchagin氏が説明するこのアプローチでは、Linuxカーネルの一部であるシバンのサポートと、Linuxユーザスペースからサポート対象バイナリ形式を拡張する機能とを利用する。手短に説明すると、Korgachin氏が勧めるのは、次のように、新たなバイナリフォーマットを登録する方法だ。
$ echo ':golang:E::go::/usr/local/bin/gorun:OC' | sudo tee /proc/sys/fs/binfmt_misc/register
:golang:E::go::/usr/local/bin/gorun:OC
これによって、次のように完全な標準.go
プログラムの実行ビットをセットできるようになる。
package main
import (
"fmt"
"os"
)
func main() {
s := "world"
if len(os.Args) > 1 {
s = os.Args[1]
}
fmt.Printf("Hello, %v!", s)
fmt.Println("")
if s == "fail" {
os.Exit(30)
}
}
and execute it with:
$ chmod u+x helloscript.go
$ ./helloscript.go
Hello, world!
$ ./helloscript.go gopher
Hello, gopher!
$ ./helloscript.go fail
Hello, fail!
$ echo $?
30
このアプローチではREPLは実現できないものの、シバンが使用できれば十分だというユースケースもあるはずだ。Korgachin氏の記事は、バイナリフォーマットがLinuxカーネル上で動作する方法についての洞察や詳細が満載なので、このトピックに興味があるのならば、ぜひ読んで頂きたい。