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

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

このコミットは、Go言語のコマンドラインツールである go コマンドの run サブコマンドに、実行するGoプログラムへの引数渡し機能を追加するものです。これにより、go run を使用してGoプログラムをコンパイル・実行する際に、そのプログラム自体が受け取るべきコマンドライン引数を -- (ダブルハイフン) を区切りとして指定できるようになります。

コミット

commit fd1322828c656813c320ab556bffe8eafeb57976
Author: Eric Eisner <eric.d.eisner@gmail.com>
Date:   Fri Jan 6 09:23:00 2012 +1100

    cmd/go: Pass arguments to command for run
    
    Command arguments are separated from input .go file arguments
    by a -- separator.
    
    R=rsc, golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/5514046

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

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

元コミット内容

cmd/go: Pass arguments to command for run

Command arguments are separated from input .go file arguments
by a -- separator.

R=rsc, golang-dev, adg
CC=golang-dev
https://golang.org/cl/5514046

変更の背景

go run コマンドは、指定されたGoソースファイルをコンパイルし、その場で実行するための便利なツールです。しかし、このコミット以前は、go run コマンド自体に渡す引数と、go run によって実行されるGoプログラムに渡す引数を区別する明確な方法がありませんでした。

例えば、go run main.go -v と実行した場合、-vgo run コマンドのオプションとして解釈されるのか、それとも main.go が受け取るべき引数として解釈されるのかが曖昧でした。特に、実行されるプログラムが独自のコマンドライン引数を必要とする場合、この曖昧さは大きな問題となります。

このコミットは、この問題を解決するために、標準的なコマンドラインの慣習である -- (ダブルハイフン) を導入し、go run コマンドの引数と、実行されるGoプログラムへの引数を明確に分離できるようにすることを目的としています。これにより、開発者は go run を使ってプログラムをテストする際に、より柔軟に引数を渡せるようになります。

前提知識の解説

go run コマンド

go run は、Go言語のツールチェインが提供するコマンドの一つで、Goのソースファイルを一時的にコンパイルし、その実行可能ファイルを即座に実行します。開発中の小さなプログラムのテストや、スクリプトのような使い方ができるため、非常に便利です。通常、go run main.go のように使用します。

コマンドライン引数

コマンドライン引数とは、プログラムを実行する際に、そのプログラムに渡される追加の情報のことです。例えば、ls -l /tmp というコマンドでは、-l/tmpls コマンドへの引数です。Goプログラムでは、os.Args スライスを通じてこれらの引数にアクセスできます。

-- (ダブルハイフン) の慣習

Unix系システムにおけるコマンドラインの一般的な慣習として、-- (ダブルハイフン) は、それ以降の文字列をオプションではなく、ファイル名やその他の非オプション引数として扱うことを示すために使用されます。これにより、ハイフンで始まるファイル名などを誤ってオプションとして解釈されることを防ぎます。このコミットでは、この慣習を go run コマンドに適用し、go run 自身のオプションと、実行されるGoプログラムへの引数を区別するために利用しています。

技術的詳細

このコミットの技術的な核心は、go run コマンドの引数解析ロジックを変更し、-- を特別な区切り文字として認識させる点にあります。

  1. action 構造体の拡張: src/cmd/go/build.go に定義されている action 構造体は、go コマンドがビルドや実行の各ステップを管理するための内部的なデータ構造です。このコミットでは、action 構造体に args []string という新しいフィールドが追加されました。これは、go run によって実行されるプログラムに渡されるべきコマンドライン引数を保持するために使用されます。

  2. go run の引数解析の変更: src/cmd/go/run.go にある runRun 関数は、go run コマンドが呼び出されたときに実行される主要な関数です。この関数内で、新しい splitArgs 関数が導入されました。 splitArgs 関数は、go run に渡されたすべての引数を走査し、-- が出現するかどうかをチェックします。

    • -- の前にある引数は、go run コマンドが処理すべきGoソースファイル名として扱われます。
    • -- の後にある引数は、実行されるGoプログラムに渡されるべき引数として扱われ、action 構造体の args フィールドに格納されます。
  3. プログラム実行時の引数渡し: src/cmd/go/run.gorunProgram メソッドは、実際にコンパイルされたGoプログラムを実行する役割を担います。このメソッドは、以前はコンパイルされたプログラムのパスのみを run 関数に渡していました。このコミットにより、action 構造体に格納された args (つまり、-- の後に指定された引数) も run 関数に渡されるように変更されました。これにより、実行されるGoプログラムがこれらの引数を受け取れるようになります。

これらの変更により、go run コマンドは、go run [goファイル] -- [プログラム引数] という形式で、Goプログラムへの引数を透過的に渡すことができるようになりました。

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

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -157,6 +157,7 @@ type action struct {
 	deps     []*action // actions that must happen before this one
 	triggers []*action // inverse of deps
 	cgo      *action   // action for cgo binary if needed
+	args     []string  // additional args for runProgram
 
 	f          func(*builder, *action) error // the action itself (nil = no-op)
 	ignoreFail bool                          // whether to run f even if dependencies fail

action 構造体に args []string フィールドが追加されました。これは、go run によって実行されるプログラムに渡されるコマンドライン引数を保持するためのものです。

src/cmd/go/run.go

--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -12,7 +12,7 @@ func init() {
 }
 
 var cmdRun = &Command{
-	UsageLine: "run [-a] [-n] [-x] gofiles...",
+	UsageLine: "run [-a] [-n] [-x] gofiles... [-- arguments...]",
 	Short:     "compile and run Go program",
 	Long: `
 Run compiles and runs the main package comprising the named Go source files.
@@ -32,16 +32,34 @@ var runX = cmdRun.Flag.Bool(\"x\", false, \"\")
 func runRun(cmd *Command, args []string) {
 	var b builder
 	b.init(*runA, *runN, *runX)
-\tp := goFilesPackage(args, \"\")
+\tfiles, args := splitArgs(args)\n+\tp := goFilesPackage(files, \"\")
 \tp.target = \"\" // must build - not up to date
 \ta1 := b.action(modeBuild, modeBuild, p)\n-\ta := &action{f: (*builder).runProgram, deps: []*action{a1}}\n+\ta := &action{f: (*builder).runProgram, args: args, deps: []*action{a1}}\n \tb.do(a)\n }\n \n // runProgram is the action for running a binary that has already
 // been compiled.  We ignore exit status.\n func (b *builder) runProgram(a *action) error {\n-\trun(a.deps[0].target)\n+\targs := append([]string{a.deps[0].target}, a.args...)\n+\trun(args...)\n \treturn nil\n }\n+\n+// Return the argument slices before and after the \"--\"\n+func splitArgs(args []string) (before, after []string) {\n+\tdashes := len(args)\n+\tfor i, arg := range args {\n+\t\tif arg == \"--\" {\n+\t\t\tdashes = i\n+\t\t\tbreak\n+\t\t}\n+\t}\n+\tbefore = args[:dashes]\n+\tif dashes < len(args) {\n+\t\tafter = args[dashes+1:]\n+\t}\n+\treturn\n+}\n```

## コアとなるコードの解説

### `src/cmd/go/run.go` の変更点詳細

1.  **`cmdRun.UsageLine` の変更**:
    `UsageLine` は、`go help run` と入力した際に表示されるコマンドの利用方法の概要です。ここに `[-- arguments...]` が追加され、`--` の後にプログラムへの引数を指定できることが明示されました。

2.  **`runRun` 関数の変更**:
    *   `files, args := splitArgs(args)`: 以前は `goFilesPackage(args, "")` で直接引数を渡していましたが、この行で新しく追加された `splitArgs` 関数を呼び出し、`go run` コマンド自身の引数 (`files`) と、実行されるGoプログラムへの引数 (`args`) を分離しています。
    *   `a := &action{f: (*builder).runProgram, args: args, deps: []*action{a1}}`: `action` 構造体を初期化する際に、`splitArgs` から得られたプログラムへの引数 `args` を、新しく追加された `action.args` フィールドに設定しています。これにより、プログラム実行時にこれらの引数が利用可能になります。

3.  **`runProgram` メソッドの変更**:
    *   `args := append([]string{a.deps[0].target}, a.args...)`: 以前は `run(a.deps[0].target)` のように、コンパイルされたプログラムのパスのみを `run` 関数に渡していました。変更後、`a.deps[0].target` (実行可能ファイルのパス) に加えて、`action` 構造体に格納されている `a.args` (プログラムへの引数) を結合し、新しい引数スライスを作成しています。
    *   `run(args...)`: 作成された引数スライスを `run` 関数に渡すことで、Goプログラムがこれらの引数を受け取って実行されるようになります。

4.  **`splitArgs` 関数の新規追加**:
    この関数は、`go run` に渡された引数スライス `args` を受け取り、`--` を区切りとして2つのスライスに分割します。
    *   `dashes := len(args)`: まず、`--` が見つからなかった場合のデフォルトとして、すべての引数を `before` スライスに含めるように `dashes` を初期化します。
    *   `for i, arg := range args`: 引数をループで走査します。
    *   `if arg == "--"`: `--` が見つかった場合、そのインデックスを `dashes` に設定し、ループを終了します。
    *   `before = args[:dashes]`: `--` の前にある引数を `before` スライスに格納します。
    *   `if dashes < len(args)`: `--` が存在した場合、`--` の次の要素から最後までを `after` スライスに格納します。
    *   `return`: `before` と `after` の2つのスライスを返します。

これらの変更により、`go run` コマンドは、Goプログラムへの引数をより直感的かつ標準的な方法で受け取ることができるようになり、開発の利便性が向上しました。

## 関連リンク

*   Go Gerrit Code Review: [https://golang.org/cl/5514046](https://golang.org/cl/5514046)

## 参考にした情報源リンク

*   Go Command Documentation: [https://go.dev/cmd/go/](https://go.dev/cmd/go/)
*   Go `os.Args` documentation: [https://pkg.go.dev/os#Args](https://pkg.go.dev/os#Args)
*   Unix/Linux Command Line Arguments (General Concept): [https://en.wikipedia.org/wiki/Command-line_interface#Arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
*   The `--` argument: [https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html)I have generated the detailed explanation of the commit as requested, following all the specified sections and formatting. The output is in Markdown format and printed to standard output.