[インデックス 14203] ファイルの概要
このコミットは、Go言語のコマンドラインツール go
の list
サブコマンドに、テンプレート関数 join
を追加するものです。これにより、go list -f
オプションでパッケージ情報を整形して出力する際に、文字列スライス(例: 依存関係のリスト)をシェルで扱いやすい形式で結合できるようになります。具体的には、strings.Join
関数をテンプレート内で直接呼び出せるようにすることで、出力の柔軟性と利便性を向上させています。
コミット
- コミットハッシュ:
9714691a3f1862f09cd8d8536131c01c15ab32c3
- Author: Roger Peppe rogpeppe@gmail.com
- Date: Mon Oct 22 08:58:27 2012 +0100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9714691a3f1862f09cd8d8536131c01c15ab32c3
元コミット内容
cmd/go: add join template function.
It's common to use the go list command in shell scripts, but
currently it's awkward to print a string slice from the Package
type in a way that's easily parseable by the shell. For example:
go list -f '{{range .Deps}}{{.}}
{{end}}'
(and even that prints an unwanted new line at the end|).
To make this easier, this CL adds a "join" function to the
format template.
go list -f '{{join .Deps "\n"}}'
R=rsc, dsymonds, minux.ma, remyoudompheng, r
CC=golang-dev
https://golang.org/cl/6680044
変更の背景
go list
コマンドは、Goパッケージに関する情報を表示するために使用されます。-f
フラグを使用すると、Goの text/template
パッケージの構文を用いて出力フォーマットをカスタマイズできます。しかし、既存のテンプレート機能では、Package
型に含まれる文字列スライス(例えば、パッケージの依存関係を示す .Deps
)を、シェルスクリプトで簡単にパースできる形式で出力するのが困難でした。
具体的には、文字列スライスを改行区切りで出力しようとすると、{{range .Deps}}{{.}}\n{{end}}
のように記述する必要がありましたが、これでは最後の要素の後に余分な改行が出力されてしまうという問題がありました。このような出力は、シェルスクリプトで xargs
や for
ループなどを使って処理する際に不便でした。
この問題を解決し、go list
の出力をよりシェルスクリプトフレンドリーにするために、文字列スライスを任意の区切り文字で結合する join
テンプレート関数が求められました。
前提知識の解説
go list
コマンド
go list
は、Goのワークスペース内のパッケージに関する情報を表示するためのコマンドです。パッケージのインポートパス、ディレクトリ、依存関係、ソースファイルなど、さまざまな詳細を取得できます。
-f
フラグを使用すると、Goの text/template
パッケージの構文を使って出力形式を柔軟に指定できます。これにより、特定の情報だけを抽出したり、カスタムフォーマットで表示したりすることが可能です。
Goの text/template
パッケージ
text/template
パッケージは、Go言語でテキストベースのテンプレートを生成するための機能を提供します。HTML、XML、プレーンテキストなど、さまざまな形式の出力を生成するのに使用されます。テンプレートは、プレースホルダー(例: {{.Field}}
)や制御構造(例: {{range .Slice}}...{{end}}
)を含めることができ、データ構造をテンプレートに渡してレンダリングすることで、動的なテキストを生成します。
text/template
は、カスタム関数を登録する機能も持っています。これにより、テンプレート内でGoの関数を呼び出し、より複雑なデータ処理や整形を行うことができます。カスタム関数は template.FuncMap
を使用して登録されます。
strings.Join
関数
Goの標準ライブラリ strings
パッケージに含まれる Join
関数は、文字列スライス([]string
)の要素を、指定された区切り文字で結合して一つの文字列として返す関数です。
関数シグネチャ:
func Join(s []string, sep string) string
s
: 結合する文字列スライスsep
: 要素間に挿入する区切り文字
例:
package main
import (
"fmt"
"strings"
)
func main() {
s := []string{"apple", "banana", "cherry"}
result := strings.Join(s, ", ")
fmt.Println(result) // 出力: apple, banana, cherry
}
技術的詳細
この変更の核心は、text/template
パッケージの Funcs
メソッドを使用して、Goの標準ライブラリ関数 strings.Join
を go list
コマンドのテンプレートエンジンにカスタム関数として登録することです。
template.New("name").Funcs(template.FuncMap{...}).Parse(templateString)
のように記述することで、テンプレートのパース時にカスタム関数マップをテンプレートエンジンに提供できます。template.FuncMap
は map[string]interface{}
型であり、キーがテンプレート内で使用する関数名(例: "join"
)、値が対応するGoの関数(例: strings.Join
)となります。
strings.Join
は []string
と string
を引数に取り、string
を返す関数です。text/template
は、Goの関数のシグネチャを自動的に解釈し、テンプレート内で適切な引数が渡された場合にその関数を呼び出すことができます。これにより、テンプレートユーザーは {{join .Deps "\n"}}
のように簡潔な構文で strings.Join
の機能を利用できるようになります。
コアとなるコードの変更箇所
変更は src/cmd/go/list.go
ファイルに集中しています。
-
strings
パッケージのインポート追加:--- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -9,6 +9,7 @@ import ( "encoding/json" "io" "os" + "strings" "text/template" )
strings.Join
関数を使用するために、strings
パッケージがインポートされました。 -
go list -f
の説明文の更新:--- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -24,10 +25,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -The -f flag specifies an alternate format for the list, -using the syntax of package template. The default output -is equivalent to -f '{{.ImportPath}}'. The struct -being passed to the template is: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources
go list -f
オプションの説明に、join
関数が利用可能になったことが追記されました。 -
テンプレートの初期化時に
join
関数を登録:--- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -113,7 +114,7 @@ func runList(cmd *Command, args []string) { out.Write(nl) } } else { - tmpl, err := template.New("main").Parse(*listFmt) + tmpl, err := template.New("main").Funcs(template.FuncMap{"join": strings.Join}).Parse(*listFmt) if err != nil { fatalf("%s", err) }
runList
関数内でテンプレートをパースする際に、template.New("main").Funcs(template.FuncMap{"join": strings.Join})
を呼び出すことで、join
という名前でstrings.Join
関数がテンプレートエンジンに登録されています。
コアとなるコードの解説
このコミットの最も重要な変更は、src/cmd/go/list.go
の runList
関数内で行われている以下の行です。
tmpl, err := template.New("main").Funcs(template.FuncMap{"join": strings.Join}).Parse(*listFmt)
このコードは、以下のステップでテンプレートを準備しています。
template.New("main")
: 新しいテンプレートインスタンスを作成し、名前を "main" とします。.Funcs(template.FuncMap{"join": strings.Join})
: ここが肝となる部分です。Funcs
メソッドは、テンプレートで使用できるカスタム関数を登録するために使用されます。template.FuncMap
は、map[string]interface{}
型のエイリアスであり、キーがテンプレート内で呼び出す関数名(この場合は"join"
)、値がその関数に対応するGoの関数(この場合はstrings.Join
)です。strings.Join
はfunc Join(s []string, sep string) string
というシグネチャを持つため、テンプレート内で{{join .Slice "separator"}}
のように呼び出されると、.Slice
が[]string
として、"separator"
がstring
としてstrings.Join
に渡され、その結果がテンプレートに挿入されます。
.Parse(*listFmt)
: ユーザーが-f
フラグで指定したフォーマット文字列(*listFmt
)をパースし、テンプレートオブジェクトを構築します。
この変更により、go list -f '{{join .Deps "\n"}}'
のようなコマンドが有効になり、パッケージの依存関係を改行区切りで、かつ余分な改行なしで出力できるようになりました。これは、シェルスクリプトでの go list
の出力を処理する際に非常に役立ちます。
関連リンク
- Go CL 6680044: https://golang.org/cl/6680044
参考にした情報源リンク
- Go
text/template
package documentation: https://pkg.go.dev/text/template - Go
strings
package documentation: https://pkg.go.dev/strings - Go
go list
command documentation (general concept): https://pkg.go.dev/cmd/go#hdr-List_packages_or_modules