[インデックス 18626] ファイルの概要
このコミットは、Goコマンドラインツール (cmd/go
) の go env
サブコマンドの出力形式を、Plan 9オペレーティングシステムの rc
シェルと互換性があるように修正するものです。特に、GOPATH
のように複数のパスコンポーネントを持つ環境変数の表現が改善され、Plan 9環境でのGo開発の利便性が向上しています。
コミット
commit ff15e5c00f7fe3fcec1277f932fbca381fd2d2ad
Author: Lucio De Re <lucio.dere@gmail.com>
Date: Mon Feb 24 19:48:06 2014 +0100
cmd/go: Plan 9 compatible "env" output
Fixes the output of go env so that variables can be set
more accurately when using Plan 9's rc shell. Specifically,
GOPATH may have multiple components and the current
representation is plain wrong. In practice, we probably
ought to change os. Getenv to produce the right result, but
that requires considerably more thought.
LGTM=rsc
R=golang-codereviews, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/66600043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ff15e5c00f7fe3fcec1277f932fbca381fd2d2ad
元コミット内容
cmd/go: Plan 9 compatible "env" output
Fixes the output of go env so that variables can be set
more accurately when using Plan 9's rc shell. Specifically,
GOPATH may have multiple components and the current
representation is plain wrong. In practice, we probably
ought to change os. Getenv to produce the right result, but
that requires considerably more thought.
変更の背景
go env
コマンドは、Goの環境変数の現在の設定を表示するために使用されます。このコマンドは、様々なシェル(bash, cmd, powershellなど)に対応した形式で出力を生成できます。しかし、従来の go env
のPlan 9 (rc
シェル) 向けの出力は、GOPATH
のように複数のパスがコロン (:
) で区切られて設定される環境変数に対して、正しく機能しないという問題がありました。
Plan 9の rc
シェルでは、複数の値をリストとして持つ環境変数を表現する際に、特定の構文(例: VAR=(val1 val2 val3)
)を使用します。従来の go env
の出力では、GOPATH
のような変数が単一の引用符で囲まれた文字列として出力されていたため、rc
シェルがこれを単一のパスとして解釈してしまい、Goツールが期待する複数のパスとして認識できませんでした。
このコミットは、この問題を解決し、go env
がPlan 9の rc
シェルで正しく環境変数を設定できるようにすることを目的としています。特に、GOPATH
のようなパスリストを、rc
シェルが正しく解釈できる形式で出力するように変更されています。コミットメッセージには、「os.Getenv
を変更して正しい結果を生成すべきだが、それはより多くの検討が必要」という言及があり、これは一時的な修正であり、より根本的な解決策が将来的に検討される可能性を示唆しています。
前提知識の解説
- Go言語の環境変数: Go言語のツールチェーンは、
GOPATH
,GOROOT
,GOOS
,GOARCH
など、様々な環境変数に依存して動作します。これらの変数は、Goのソースコードの場所、ビルドターゲットのOSやアーキテクチャなどを指定します。 GOPATH
: Goのワークスペースのルートディレクトリを指定する環境変数です。Go 1.11以降のGo Modulesの導入によりその重要性は低下しましたが、それ以前のバージョンや特定のレガシープロジェクトでは依然として重要な役割を果たします。複数のディレクトリをコロン (:
) で区切って指定することができ、Goツールはこれらのディレクトリ内でソースコード、パッケージ、実行可能ファイルを検索します。go env
コマンド: Goツールチェーンの一部であり、Goの環境変数の現在の設定を表示するコマンドです。特定のシェル形式 (-s
フラグで指定) で出力することも可能です。- Plan 9: ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソースをファイルとして表現するという思想を持っています。
- Plan 9
rc
シェル: Plan 9の標準シェルです。Unix系のシェル(bash, zshなど)とは異なる独自の構文と哲学を持っています。特に、環境変数の扱い方やリストの表現方法に特徴があります。rc
シェルでは、スペース区切りの要素を括弧で囲むことでリストを表現します。例:path=(/bin /usr/bin)
\x00
(NULLバイト): プログラミングにおいて、文字列の終端を示すためによく使われる特殊な文字です。Goの内部では、GOPATH
のような複数のパスを持つ環境変数を表現するために、パスの各要素をNULLバイトで区切って格納することがあります。これは、OSの環境変数として設定される際にはコロンなどの区切り文字に変換されますが、内部表現ではNULLバイトが使われることがあります。
技術的詳細
このコミットの核心は、src/cmd/go/env.go
ファイル内の runEnv
関数におけるPlan 9 (rc
シェル) 向けの出力ロジックの変更です。
変更前は、Plan 9向けの出力は単純に fmt.Printf("%s='%s'\\n", e.name, strings.Replace(e.value, "'", "''", -1))
のように、環境変数の値を単一の引用符で囲んで出力していました。これは、GOPATH
のように複数のパスがコロンで区切られた文字列として格納されている場合、rc
シェルがこれを単一の文字列として解釈してしまい、期待通りの動作をしませんでした。
変更後は、以下のロジックが追加されました。
- NULLバイトのチェック:
strings.IndexByte(e.value, '\x00') < 0
という条件で、環境変数の値 (e.value
) にNULLバイト (\x00
) が含まれているかどうかをチェックします。- Goの内部では、
GOPATH
のような複数のパスを持つ環境変数は、各パスがNULLバイトで区切られた単一の文字列として表現されることがあります。このチェックは、その内部表現を検出するためのものです。
- Goの内部では、
- NULLバイトがない場合: NULLバイトが含まれていない場合(通常の単一値の環境変数など)は、変更前と同様に単一の引用符で囲まれた文字列として出力します。
fmt.Printf("%s='%s'\\n", e.name, strings.Replace(e.value, "'", "''", -1))
- NULLバイトがある場合: NULLバイトが含まれている場合(
GOPATH
のようなパスリスト)は、値をNULLバイトで分割し、rc
シェルのリスト構文 (VAR=(val1 val2 val3)
) で出力します。v := strings.Split(e.value, "\x00")
で、NULLバイトを区切り文字として文字列をスライスに分割します。fmt.Printf("%s=(", e.name)
で、変数名と開き括弧を出力します。- ループ
for x, s := range v
で、分割された各パス要素を処理します。 if x > 0 { fmt.Printf(" ") }
で、最初の要素以外はスペースを挿入し、各要素をスペースで区切ります。fmt.Printf("%s", s)
で、各パス要素を出力します。fmt.Printf(")\\n")
で、閉じ括弧と改行を出力します。
この変更により、GOPATH=/path/to/go/src:/path/to/another/src
のような値が、Plan 9の rc
シェルで GOPATH=(/path/to/go/src /path/to/another/src)
のように正しく解釈される形式で出力されるようになります。
コアとなるコードの変更箇所
src/cmd/go/env.go
ファイルの runEnv
関数内、case "plan9":
ブロックが変更されています。
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -91,7 +91,19 @@ func runEnv(cmd *Command, args []string) {
default:
fmt.Printf("%s=\"%s\"\\n\", e.name, e.value)
case "plan9":
- fmt.Printf("%s='%s'\\n\", e.name, strings.Replace(e.value, "'", "''", -1))
+ if strings.IndexByte(e.value, '\x00') < 0 {
+ fmt.Printf("%s='%s'\\n\", e.name, strings.Replace(e.value, "'", "''", -1))
+ } else {
+ v := strings.Split(e.value, "\x00")
+ fmt.Printf("%s=(", e.name)
+ for x, s := range v {
+ if x > 0 {
+ fmt.Printf(" ")
+ }
+ fmt.Printf("%s", s)
+ }
+ fmt.Printf(")\\n")
+ }
case "windows":
fmt.Printf("set %s=%s\\n\", e.name, e.value)
}
コアとなるコードの解説
変更されたコードブロックは、go env
コマンドがPlan 9 (rc
シェル) 向けの出力を生成する際のロジックを定義しています。
e.name
: 環境変数名(例:GOPATH
)e.value
: 環境変数の値(例:/home/user/go:/usr/local/go
のような文字列、または内部的にはNULLバイトで区切られた文字列)
新しいロジックは、まず e.value
にNULLバイト (\x00
) が含まれているかどうかを確認します。
-
if strings.IndexByte(e.value, '\x00') < 0
:strings.IndexByte
は、文字列e.value
内で指定されたバイト (\x00
) が最初に出現するインデックスを返します。見つからない場合は-1
を返します。- この条件は、
e.value
にNULLバイトが含まれていない場合(つまり、単一の値を表す環境変数である可能性が高い場合)に真となります。 - この場合、以前と同様に
fmt.Printf("%s='%s'\\n", e.name, strings.Replace(e.value, "'", "''", -1))
を実行します。これは、変数名をシングルクォートで囲まれた値として出力し、値に含まれるシングルクォートは二重にすることでエスケープします。
-
else
ブロック:e.value
にNULLバイトが含まれている場合(つまり、GOPATH
のように複数のパスがNULLバイトで区切られて格納されている場合)に実行されます。v := strings.Split(e.value, "\x00")
:e.value
をNULLバイトで分割し、結果の文字列スライスをv
に格納します。これにより、各パスが個別の要素として扱われます。fmt.Printf("%s=(", e.name)
:rc
シェルのリスト構文の開始部分 (VAR=(
) を出力します。for x, s := range v
: 分割されたパス要素s
をループ処理します。x
はインデックスです。if x > 0 { fmt.Printf(" ") }
: 最初の要素以外の場合、要素間にスペースを挿入します。これはrc
シェルのリスト構文で要素を区切るために必要です。fmt.Printf("%s", s)
: 現在のパス要素s
を出力します。fmt.Printf(")\\n")
:rc
シェルのリスト構文の終了部分 ()
) と改行を出力します。
この変更により、go env
はPlan 9の rc
シェルに対して、単一値の環境変数は通常の文字列として、複数パスの環境変数はリストとして、それぞれ正しく解釈される形式で出力できるようになりました。
関連リンク
- Go言語の環境変数: https://go.dev/doc/goenv
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Plan 9
rc
シェル: https://9p.io/plan9/man/4/rc.html (Plan 9のmanページ) - Go Modules (GOPATHの代替): https://go.dev/blog/using-go-modules
参考にした情報源リンク
- コミットメッセージとコード差分 (
git diff
) - Go言語公式ドキュメント
- Plan 9のドキュメント (特に
rc
シェルに関する情報) - Go言語の環境変数に関する一般的な知識
strings.IndexByte
およびstrings.Split
関数のGo言語ドキュメント- Goの環境変数が内部的にNULLバイトで区切られることに関する情報 (Goのソースコードや関連する議論から)