go installで迷わない:@version / go get / PATH をまとめて整理
「go installしたはずなのにコマンドが見つからない」
「@latestを付けるのはなぜ?」
「go getと何が違うの?」
——Goを触り始めた頃、この3点で何度も混乱しました。
原因はgo installが難しいというより、Go Modules(モジュール + バージョン)、インストール先(GOBIN/GOPATH)、そしてPATHといった前提が一度に出てくるため、最初はそれらが見えにくいからです。
この記事では、Go Modules前提でgo installが何をするコマンドかを体系的に整理し、「なぜそうなるか」まで説明できる状態を目指します。
go installは何をするコマンドか
go installは「指定したプログラムをビルドして、コマンドとして実行できる形(実行ファイル)で所定の場所に置く」コマンドです。
置き場所はGOBIN(未設定ならGOPATH/bin)です。
そして go install <pkg>@<version> は、「どのコマンドを、どのバージョンで入れるか」を1行で指定する書き方です。
この書き方のポイントは、いま開いているプロジェクトの依存(go.mod)とは切り離してコマンドを入れられることです。
ここでの意味はこうです。
-
<pkg>: 入れたいコマンドの場所(import path) -
<version>: そのコマンドのバージョン
例:
-
go install example.com/some-tool@v1.2.3
→ some-toolというコマンドをv1.2.3のバージョンで入れる
この形式の利点は2つあります。
-
go.modを汚しにくい:ツール導入のために、プロジェクトの依存が増えたり更新されたりしにくい - 再現しやすい:
@v1.2.3のようにバージョンを固定すれば、別の環境でも同じバージョンのコマンドを入れられる
なぜgo installは分かりにくいのか
分かりづらい理由は、まず「何のために使うのか」が分からないまま使いがちだからです。
go installは、基本的に「ターミナルで実行するコマンド(実行ファイル)を入れる」ために使います。
次に「誰にとって、どう嬉しいのか」が見えにくいのも混乱の原因です。
例えばgo install <pkg>@<version>は、次のような場面で役に立ちます。
- チームで同じバージョンのツール(コマンド)を揃えたい
- プロジェクトの依存(
go.mod)を増やしたり更新したりせずに、ツールだけ入れたい
用途が見えると、go installでハマりやすいポイントも整理できます。
- どこに入るか:
GOBIN(未設定ならGOPATH/bin)に実行ファイルが置かれる。そこがPATHに入っていないとcommand not foundになる - どのバージョンを入れるか:
@vX.Y.Z(固定)や@latest(その時点の最新)で、どのソースからビルドして入れるかが変わる
go getとの違い
ここは初心者が最もつまずくポイントです。結論から言うと、目的が違います。
-
go get
基本的に「このプロジェクト(あなたのモジュール)の依存関係を更新・追加する」ための操作です。つまりgo.modに影響する領域です。 -
go install <pkg>@<version>
「プロジェクトの依存とは独立に、指定バージョンのコマンドを手元に入れる」ための操作です。重要なのは、ツール導入のためにプロジェクトの依存を勝手に動かさないことです。
この分離を意識すると、「いまやりたいのは依存更新?それともツール導入?」で迷いが減ります。
go installの基本構文と@latest/@versionの意味
go installは、書き方によってモードが変わります。
-
ローカル(相対パス)指定(例:
go install ./cmd/hello)- 手元のソース(現在のモジュール内のコード)をビルドしてインストールします。
- この場合、
@versionは出てきません(ソースがローカルで確定しているため)。
-
バージョン付き指定(例:
go install example.com/mod/cmd/hello@v0.1.0)-
@v0.1.0で「このバージョンのソースを使う」と決めて、取得→ビルド→インストールします。 - どのバージョンを入れたかが明確になるので、別の環境でも同じものを入れやすくなります。
-
-
latest指定(例:
go install example.com/mod/cmd/hello@latest)- 「現時点の最新バージョン」を取ります。便利ですが、別の環境(別の人/別マシン/CI)で同じ結果になる保証は弱くなります。
なぜ@versionが必要なのか?
Go Modulesでは、リモートのコードは「モジュール + バージョン」で初めて一意に決まります。
@vX.Y.Zのようにバージョンを固定すると、別の人/別マシン/CIでも同じソースを取りやすくなり、結果を揃えやすくなります。
コマンドを例にした「何が起きているか」の説明
サンプルとしてhelloという最小コマンドを用意します。
--versionオプションで、ビルド情報(モジュール情報)を表示できるようにしています。
ここで観察したいのは「go installが実際に何をしているか」です。
go installを概念的に分解すると、次の流れです。
-
- どのコマンドを入れるか決める
例:./cmd/hello(ローカル)/example.com/mod/cmd/hello(リモート)
- どのコマンドを入れるか決める
-
- どのバージョンを入れるか決める
ローカル指定(./cmd/hello)なら不要で、@v0.1.0や@latestのようにバージョンを指定します。
- どのバージョンを入れるか決める
-
- ビルド
mainパッケージがコンパイルされ、実行ファイルが作られます。
- ビルド
-
- 配置(インストール)
実行ファイルがGOBIN(未設定ならGOPATH/bin)に置かれます。
- 配置(インストール)
-
- 実行できるかはPATH次第
PATHにそのディレクトリが入っていなければ、シェルはコマンドを見つけられません。
- 実行できるかはPATH次第
PATH/GOBIN/GOPATHの関係を概念的に説明
「暗記」ではなく「役割」で理解すると安定します。
-
GOBIN:go installがバイナリ(コマンド)を置く先(固定したいときに設定) -
GOPATH:Goの作業ディレクトリの基準。GOBINが未設定ならインストール先はGOPATH/bin -
PATH: シェルがコマンドを探す検索パス。GOBIN/GOPATH/binが PATH に入っていないと、インストールしても実行できない
最重要:go installの成功は「配置に成功した」だけで、「シェルが見つけられるか」は PATH の問題です。
よくある詰まりポイントと考え方
- 詰まり1:そもそも「何のために使うのか」「誰が嬉しいのか」が分からない
- 用途の例:開発を楽にするコマンドを入れる
- gofmt/goimportsの整形
- golangci-lintのチェック
- stringerやmockgen等のコード生成
- チーム内で共通の自作ツールを配布して使う
- 用途の例:開発を楽にするコマンドを入れる
- 詰まり2:
go installしたのにcommand not found- 考え方:インストール先を確認 → PATH を確認
- まず
go env GOBINとgo env GOPATHで「どこに入る想定か」を把握し、そのディレクトリがPATHに入っているかを確認する
- 詰まり3:
@latestを付けろと言われる理由が分からない- 考え方:「どのバージョンを入れるか」を明確にするため
- 同じ結果に揃えたいなら
@latestより@vX.Y.Zを優先する
- 詰まり4:cloneしたリポジトリで
go install ./cmd/...すれば十分では?- 考え方:手元で動かすだけならそれでもOK。ただし「別の環境でも同じものを入れる」説明をしたいなら
@vX.Y.Zで固定した方が伝わりやすい
- 考え方:手元で動かすだけならそれでもOK。ただし「別の環境でも同じものを入れる」説明をしたいなら
- 詰まり5:インストール先に書けない
- 考え方:
GOBINを書き込み可能な場所に固定する
- 考え方:
まとめ
go installは「mainをビルドしてGOBIN(未設定ならGOPATH/bin)に置く」コマンドです。
<pkg>@<version>を使うと、依存管理と切り離して、再現しやすい形でコマンドをインストールできます。
Discussion