Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15758] ファイルの概要

このコミットは、Go言語のコマンドラインツール (cmd/go) における GOPATH 環境変数のパス検証ロジックの変更に関するものです。具体的には、GOPATH のエントリにチルダ (~) 文字が含まれている場合のチェックを緩和し、パスの先頭にチルダがある場合のみエラーとするように修正しています。

コミット

  • コミットハッシュ: ffbcd89f629509eef4524a1c824c4cce507ed6f1
  • 作者: Russ Cox rsc@golang.org
  • コミット日時: Wed Mar 13 23:32:12 2013 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ffbcd89f629509eef4524a1c824c4cce507ed6f1

元コミット内容

cmd/go: allow ~ in middle of path, just not at beginning

An earlier CL disallowed ~ anywhere in GOPATH, to avoid
problems with GOPATH='~/home' instead of GOPATH=~/home.
But ~ is only special in the shell at the beginning of each of
the paths in the list, and some paths do have ~ in the middle.
So relax the requirement slightly.

Fixes #4140.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/7799045

変更の背景

以前の変更(CL: Change List)では、GOPATH 環境変数にチルダ (~) が含まれていると、それがどこであってもエラーとしていました。これは、シェルがチルダをユーザーのホームディレクトリのエイリアスとして解釈する挙動(例: GOPATH='~/home'GOPATH=/home/user/home と解釈されることを意図せず、GOPATH=/home/user/home となることを避けるため)による問題を防ぐ目的がありました。

しかし、チルダがシェルによって特別に解釈されるのは、パスの先頭にある場合のみです。パスの途中にチルダが含まれていても、それは通常の文字として扱われ、特別な意味を持ちません。例えば、/tmp/git-1.8.2~rc3 のようなパスは、バージョン管理システムのリリースタグなどで一般的に使用される形式であり、このチルダはホームディレクトリを指すものではありません。

このため、以前の厳しすぎるチェックは、正当なパスまでエラーとしてしまうという問題を引き起こしていました。このコミットは、この過剰な制限を緩和し、チルダがパスの先頭にある場合のみエラーとするように変更することで、実用性と安全性のバランスを取ることを目的としています。これにより、GOPATH の柔軟性が向上し、より多様なパス表現に対応できるようになります。

前提知識の解説

GOPATH

GOPATH は、Go言語のワークスペースのルートディレクトリを指定する環境変数です。Go 1.11以降のGo Modulesの導入によりその役割は変化しましたが、このコミットが作成された2013年当時は、Goのソースコード、コンパイルされたパッケージ、実行可能バイナリが配置される主要な場所でした。GOPATH は複数のパスをコロン (:) またはセミコロン (;) で区切って指定することができ、Goツールはこれらのパスを順に検索してパッケージやコマンドを見つけます。

シェルにおけるチルダ (~) の展開

Unix系シェル(Bash, Zshなど)において、チルダ (~) は特別な意味を持つメタキャラクターです。

  • パスの先頭: チルダがパスの先頭にある場合、それは現在のユーザーのホームディレクトリに展開されます。例えば、~/go/home/youruser/go のように展開されます。
  • ユーザー名の指定: ~username の形式でチルダの後にユーザー名を続けると、そのユーザーのホームディレクトリに展開されます。例えば、~root/bin/root/bin のように展開されます。
  • パスの途中: チルダがパスの途中にある場合(例: /tmp/foo~bar)、シェルはこれを特別なメタキャラクターとして解釈せず、単なる文字として扱います。

このコミットの背景にある問題は、Goツールがシェルによるチルダの展開を考慮せずに、パスの途中のチルダも先頭のチルダと同じように「シェルメタキャラクター」として一律に扱っていた点にあります。

技術的詳細

このコミットは、src/cmd/go/main.go ファイル内の GOPATH の検証ロジックを変更しています。

変更前は、strings.Contains(p, "~") を使用して、GOPATH の各エントリ (p) にチルダ (~) が含まれているかどうかをチェックしていました。もしチルダが含まれており、かつOSがWindowsではない場合(Windowsではチルダのパス展開の挙動が異なるため)、エラーメッセージを出力してプログラムを終了していました。

変更後は、strings.HasPrefix(p, "~") を使用するように変更されています。これにより、チルダがパスの「先頭」にある場合のみエラーとして扱われるようになります。パスの途中にチルダが含まれていても、それはエラーとはなりません。

この変更は、シェルがチルダを特別に解釈する挙動がパスの先頭に限定されるという事実に基づいています。これにより、/tmp/git-1.8.2~rc3 のような正当なパスが誤って拒否されることを防ぎつつ、~/home のような意図しないパス展開による問題を回避するという、より正確な検証が可能になります。

また、コード内のコメントも更新され、HasPrefix を使用する理由が明確に説明されています。

コアとなるコードの変更箇所

--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -130,8 +130,11 @@ func main() {
 		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\\n", gopath)
 	} else {
 		for _, p := range filepath.SplitList(gopath) {
-			if strings.Contains(p, "~") && runtime.GOOS != "windows" {
-				fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\\n", p)
+			// Note: using HasPrefix instead of Contains because a ~ can appear
+			// in the middle of directory elements, such as /tmp/git-1.8.2~rc3
+			// or C:\\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
+			if strings.HasPrefix(p, "~") {
+				fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\\n", p)
 				os.Exit(2)
 			}
 			if build.IsLocalImport(p) {

コアとなるコードの解説

上記の差分は、src/cmd/go/main.go ファイル内の main 関数の一部を示しています。この部分は、GOPATH 環境変数の値を解析し、そのエントリを検証するロジックです。

  1. for _, p := range filepath.SplitList(gopath): GOPATH は複数のパスをコロン (:) またはセミコロン (;) で区切って指定できるため、filepath.SplitList 関数を使ってそれらを個々のパスに分割し、それぞれのパス p についてループ処理を行います。

  2. 変更前の条件分岐:

    if strings.Contains(p, "~") && runtime.GOOS != "windows" {
        fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\\n", p)
        os.Exit(2)
    }
    
    • strings.Contains(p, "~"): この行が変更の核心です。以前は、GOPATH のエントリ pどこかにチルダ (~) が含まれているかをチェックしていました。
    • runtime.GOOS != "windows": Windowsオペレーティングシステムでは、チルダのパス展開の挙動がUnix系OSとは異なるため、このチェックはWindows以外でのみ適用されていました。
    • もし条件が真であれば、エラーメッセージ「go: GOPATH entry cannot contain shell metacharacter '~': %q」を標準エラー出力に表示し、プログラムを終了コード2で終了していました。
  3. 変更後の条件分岐:

    // Note: using HasPrefix instead of Contains because a ~ can appear
    // in the middle of directory elements, such as /tmp/git-1.8.2~rc3
    // or C:\\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
    if strings.HasPrefix(p, "~") {
        fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\\n", p)
        os.Exit(2)
    }
    
    • strings.HasPrefix(p, "~"): この行が変更点です。strings.Contains から strings.HasPrefix に変更されました。これにより、GOPATH のエントリ pチルダ (~) で始まっているかどうかのみをチェックするようになりました。
    • runtime.GOOS != "windows" の条件が削除されました。これは、Windowsにおいてもパスの先頭のチルダは特別な意味を持つ可能性があるため、より一般的なチェックになったことを示唆しています。ただし、Windowsのファイルシステムではチルダがショートファイル名(8.3形式)の一部として使われることがあり、その場合はパスの途中にも現れるため、この変更はWindows環境での互換性も考慮している可能性があります。
    • エラーメッセージも「go: GOPATH entry cannot start with shell metacharacter '~': %q」に変更され、チルダがパスの先頭にある場合にのみ問題となることを明確にしています。
    • 新しいコメントが追加され、HasPrefix を使用する理由と、パスの途中にチルダが含まれる正当なケース(例: /tmp/git-1.8.2~rc3)が説明されています。

この変更により、Goツールはシェルがチルダを解釈する実際の挙動に合わせて GOPATH の検証を行うようになり、より堅牢で実用的なパス処理を実現しています。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/ffbcd89f629509eef4524a1c824c4cce507ed6f1
  • Go CL (Change List): https://golang.org/cl/7799045
  • 関連するGo Issue (Fixes #4140): このコミットメッセージに記載されている #4140 は、GoプロジェクトのIssueトラッカーにおける特定のバグ報告や機能要求を指します。Web検索ではCoreDNSのIssueがヒットしましたが、これはGoプロジェクト自体のIssueとは異なります。このコミットの文脈から、GOPATH のチルダに関する問題が報告されていたものと推測されます。

参考にした情報源リンク