⚙️

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を概念的に分解すると、次の流れです。

    1. どのコマンドを入れるか決める
      例: ./cmd/hello(ローカル)/example.com/mod/cmd/hello(リモート)
    1. どのバージョンを入れるか決める
      ローカル指定(./cmd/hello)なら不要で、@v0.1.0や@latestのようにバージョンを指定します。
    1. ビルド
      mainパッケージがコンパイルされ、実行ファイルが作られます。
    1. 配置(インストール)
      実行ファイルがGOBIN(未設定ならGOPATH/bin)に置かれます。
    1. 実行できるかは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で固定した方が伝わりやすい
  • 詰まり5:インストール先に書けない
    • 考え方:GOBINを書き込み可能な場所に固定する

まとめ

go installは「mainをビルドしてGOBIN(未設定ならGOPATH/bin)に置く」コマンドです。
<pkg>@<version>を使うと、依存管理と切り離して、再現しやすい形でコマンドをインストールできます。

サンプルコード(Git リンク)

Discussion