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

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

このコミットは、Go言語のコマンドラインツール(cmd/go)において、デバッグ出力(特にgo test -xコマンド使用時)の可読性を向上させるための変更です。具体的には、コマンドライン引数に空の文字列やスペースを含む文字列が含まれる場合に、それらを適切にクォート(引用符で囲む)することで、出力がより明確で理解しやすくなるように修正されています。

コミット

  • コミットハッシュ: ce64f7365ff814087fc843bcaa8267e236692939
  • 作者: Volker Dobler dr.volker.dobler@gmail.com
  • 日付: Fri Apr 12 14:04:00 2013 -0700

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

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

元コミット内容

cmd/go: quote command line arguments in debug output

Debug output from go test -x may contain empty arguments.
This CL quotes arguments if needed. E.g. the output of
go test -x is now
  .../6g -o ./_go_.6 -p testmain -complete -D "" -I . -I $WORK ./_testmain.go
which is easier to grasp.

R=golang-dev, bradfitz, minux.ma, r
CC=golang-dev
https://golang.org/cl/8633043

変更の背景

go test -xコマンドは、テスト実行時にGoツールが内部的に実行するコマンド(コンパイラ、リンカなど)とその引数を詳細に表示するデバッグオプションです。しかし、このデバッグ出力において、コマンドライン引数の中に空の文字列("")やスペースを含む文字列が存在する場合、それらが適切にクォートされずに表示されることがありました。

例えば、-D ""のような引数が、クォートなしで表示されると、それが本当に空の文字列引数なのか、それとも単に引数が存在しないのか、あるいは別の意味を持つのかが曖昧になり、デバッグ作業の妨げとなっていました。このコミットは、このような曖昧さを解消し、デバッグ出力の可読性と理解しやすさを向上させることを目的としています。引数を必要に応じてクォートすることで、ユーザーはGoツールが実際にどのようなコマンドと引数で内部プロセスを呼び出しているかを正確に把握できるようになります。

前提知識の解説

go test -x コマンド

go testはGo言語のテストを実行するためのコマンドです。-xフラグを付けると、テストのビルドと実行の過程でGoツールチェインが内部的に実行するすべてのコマンド(例: go tool compile, go tool link, go tool asmなど)とその引数が標準エラー出力に表示されます。これは、ビルドプロセスやテストの実行フローを詳細にデバッグしたい場合に非常に有用です。

コマンドライン引数のクォート

シェル(Bash, Zsh, PowerShellなど)において、コマンドライン引数にスペースや特殊文字が含まれる場合、それらを単一の引数として扱うためにクォート(引用符、通常はダブルクォート"またはシングルクォート')で囲む必要があります。例えば、mycommand hello worldhelloworldの2つの引数として解釈されますが、mycommand "hello world"hello worldという1つの引数として解釈されます。

また、空の文字列を明示的に引数として渡したい場合も、mycommand ""のようにクォートが必要です。クォートがないと、シェルは空の文字列を引数として認識しないため、引数の数が変わってしまう可能性があります。

cmd/go パッケージ

cmd/goはGo言語の標準ライブラリの一部であり、goコマンド自体を実装しているパッケージです。go buildgo rungo testなど、私たちが日常的に使用するすべてのgoコマンドの機能は、このパッケージとそのサブパッケージによって提供されています。このコミットで変更されたsrc/cmd/go/build.goファイルは、Goのビルドプロセスに関連するロジックを扱っています。

Go言語のstringsパッケージとstrconvパッケージ

  • stringsパッケージ: 文字列操作のためのユーティリティ関数を提供します。このコミットでは、strings.Joinが変更前は使用されていましたが、これは文字列スライスを結合する関数です。
  • strconvパッケージ: 文字列と基本的なデータ型(数値、ブール値など)の間での変換機能を提供します。特に、strconv.Quote(s string)関数は、与えられた文字列sをGoの文字列リテラルとして適切にクォートした形式で返します。これは、文字列に特殊文字が含まれる場合に、その文字列を安全に表現するために使用されます。

技術的詳細

このコミットの主要な変更点は、cmd/go/build.goファイルにjoinUnambiguouslyという新しいヘルパー関数が導入されたことです。この関数は、コマンドライン引数のスライス([]string)を受け取り、それらをスペースで結合して1つの文字列として返しますが、その際に必要に応じて個々の引数をクォートします。

joinUnambiguously関数のロジックは以下の通りです。

  1. bytes.Bufferを使用して、効率的に結果文字列を構築します。
  2. 引数のスライスaをループ処理します。
  3. 各引数sについて、まずstrconv.Quote(s)を呼び出して、その引数をGoの文字列リテラルとしてクォートした形式qを取得します。
  4. 以下のいずれかの条件が真である場合、元の引数sの代わりにクォートされた形式qを使用します。
    • s == "" (引数が空文字列の場合)
    • strings.Contains(s, " ") (引数にスペースが含まれる場合)
    • len(q) > len(s)+2 (クォートされた文字列qの長さが、元の文字列sの長さより2文字以上長い場合。これは、strconv.Quoteがエスケープ処理を行った結果、クォート文字(")以外の文字が追加されたことを意味し、例えば改行文字などが含まれる場合に発生します。このような場合も、クォートすることで曖昧さを排除します。)
  5. 上記の条件に当てはまらない場合は、元の引数sをそのまま使用します。
  6. 各引数の間にスペースを挿入して結合します。

このロジックにより、空の引数やスペースを含む引数が常にクォートされ、デバッグ出力の曖昧さが解消されます。また、strconv.Quoteがエスケープ処理を行うような特殊なケースでも、出力が明確になるように配慮されています。

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

diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 2b35136081..9b90728a72 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1237,7 +1237,7 @@ func (b *builder) processOutput(out []byte) string {
 func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) {
  	cmdline := stringList(cmdargs...)\n \tif buildN || buildX {\n-\t\tb.showcmd(dir, \"%s\", strings.Join(cmdline, \" \"))\n+\t\tb.showcmd(dir, \"%s\", joinUnambiguously(cmdline))\n \t\tif buildN {\n \t\t\treturn nil, nil\n \t\t}\n@@ -1304,6 +1304,24 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter\n \t}\n }\n \n+// joinUnambiguously prints the slice, quoting where necessary to make the\n+// output unambiguous.\n+func joinUnambiguously(a []string) string {\n+\tvar buf bytes.Buffer\n+\tfor i, s := range a {\n+\t\tif i > 0 {\n+\t\t\tbuf.WriteByte(\' \')\n+\t\t}\n+\t\tq := strconv.Quote(s)\n+\t\tif s == \"\" || strings.Contains(s, \" \") || len(q) > len(s)+2 {\n+\t\t\tbuf.WriteString(q)\n+\t\t} else {\n+\t\t\tbuf.WriteString(s)\n+\t\t}\n+\t}\n+\treturn buf.String()\n+}\n+\n // mkdir makes the named directory.\n func (b *builder) mkdir(dir string) error {\n \tb.exec.Lock()\n```

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

### `func (b *builder) runOut(...)` の変更

`runOut`関数は、Goツールチェインが外部コマンドを実行する際に使用される内部関数です。この関数内で、デバッグ出力(`buildN`または`buildX`フラグが有効な場合)のためにコマンドライン引数を文字列として結合する部分が変更されました。

変更前:
```go
b.showcmd(dir, "%s", strings.Join(cmdline, " "))

ここでは、strings.Joinを使用して、引数スライスcmdlineをスペースで単純に結合していました。この方法では、空の引数やスペースを含む引数が適切にクォートされず、出力が曖昧になる可能性がありました。

変更後:

b.showcmd(dir, "%s", joinUnambiguously(cmdline))

新しく導入されたjoinUnambiguously関数が呼び出されるようになりました。これにより、引数が結合される前に、必要に応じてクォート処理が施され、デバッグ出力の可読性が向上します。

func joinUnambiguously(a []string) string の追加

この新しい関数は、引数のスライスaを受け取り、それらを結合して曖昧さのない文字列を生成します。

func joinUnambiguously(a []string) string {
	var buf bytes.Buffer // 効率的な文字列構築のためにbytes.Bufferを使用
	for i, s := range a {
		if i > 0 {
			buf.WriteByte(' ') // 最初の要素以外はスペースを追加
		}
		q := strconv.Quote(s) // 引数sをGoの文字列リテラルとしてクォートした形式を取得
		// 以下のいずれかの条件が真の場合、クォートされた形式qを使用
		// 1. 引数が空文字列の場合 (例: "")
		// 2. 引数にスペースが含まれる場合 (例: "hello world")
		// 3. クォートされた文字列の長さが元の文字列+2 (両端のクォート)よりも長い場合
		//    これは、strconv.Quoteがエスケープ処理を行ったことを意味し、
		//    例えば改行文字などが含まれる場合に発生する。
		if s == "" || strings.Contains(s, " ") || len(q) > len(s)+2 {
			buf.WriteString(q)
		} else {
			buf.WriteString(s) // それ以外の場合は元の文字列をそのまま使用
		}
	}
	return buf.String() // 構築された文字列を返す
}

この関数は、各引数を個別に評価し、その内容に基づいてクォートするかどうかを決定します。これにより、デバッグ出力がより正確で理解しやすいものになります。

関連リンク

参考にした情報源リンク

  • Go言語のgo testコマンドに関する公式ドキュメントやブログ記事
  • Go言語のstringsパッケージとstrconvパッケージの公式ドキュメント
  • シェルにおけるコマンドライン引数のクォートに関する一般的な情報

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

このコミットは、Go言語のコマンドラインツール(cmd/go)において、デバッグ出力(特にgo test -xコマンド使用時)の可読性を向上させるための変更です。具体的には、コマンドライン引数に空の文字列やスペースを含む文字列が含まれる場合に、それらを適切にクォート(引用符で囲む)することで、出力がより明確で理解しやすくなるように修正されています。

コミット

  • コミットハッシュ: ce64f7365ff814087fc843bcaa8267e236692939
  • 作者: Volker Dobler dr.volker.dobler@gmail.com
  • 日付: Fri Apr 12 14:04:00 2013 -0700

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

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

元コミット内容

cmd/go: quote command line arguments in debug output

Debug output from go test -x may contain empty arguments.
This CL quotes arguments if needed. E.g. the output of
go test -x is now
  .../6g -o ./_go_.6 -p testmain -complete -D "" -I . -I $WORK ./_testmain.go
which is easier to grasp.

R=golang-dev, bradfitz, minux.ma, r
CC=golang-dev
https://golang.org/cl/8633043

変更の背景

go test -xコマンドは、テスト実行時にGoツールが内部的に実行するコマンド(コンパイラ、リンカなど)とその引数を詳細に表示するデバッグオプションです。しかし、このデバッグ出力において、コマンドライン引数の中に空の文字列("")やスペースを含む文字列が存在する場合、それらが適切にクォートされずに表示されることがありました。

例えば、-D ""のような引数が、クォートなしで表示されると、それが本当に空の文字列引数なのか、それとも単に引数が存在しないのか、あるいは別の意味を持つのかが曖昧になり、デバッグ作業の妨げとなっていました。このコミットは、このような曖昧さを解消し、デバッグ出力の可読性と理解しやすさを向上させることを目的としています。引数を必要に応じてクォートすることで、ユーザーはGoツールが実際にどのようなコマンドと引数で内部プロセスを呼び出しているかを正確に把握できるようになります。

前提知識の解説

go test -x コマンド

go testはGo言語のテストを実行するためのコマンドです。-xフラグを付けると、テストのビルドと実行の過程でGoツールチェインが内部的に実行するすべてのコマンド(例: go tool compile, go tool link, go tool asmなど)とその引数が標準エラー出力に表示されます。これは、ビルドプロセスやテストの実行フローを詳細にデバッグしたい場合に非常に有用です。

コマンドライン引数のクォート

シェル(Bash, Zsh, PowerShellなど)において、コマンドライン引数にスペースや特殊文字が含まれる場合、それらを単一の引数として扱うためにクォート(引用符、通常はダブルクォート"またはシングルクォート')で囲む必要があります。例えば、mycommand hello worldhelloworldの2つの引数として解釈されますが、mycommand "hello world"hello worldという1つの引数として解釈されます。

また、空の文字列を明示的に引数として渡したい場合も、mycommand ""のようにクォートが必要です。クォートがないと、シェルは空の文字列を引数として認識しないため、引数の数が変わってしまう可能性があります。

cmd/go パッケージ

cmd/goはGo言語の標準ライブラリの一部であり、goコマンド自体を実装しているパッケージです。go buildgo rungo testなど、私たちが日常的に使用するすべてのgoコマンドの機能は、このパッケージとそのサブパッケージによって提供されています。このコミットで変更されたsrc/cmd/go/build.goファイルは、Goのビルドプロセスに関連するロジックを扱っています。

Go言語のstringsパッケージとstrconvパッケージ

  • stringsパッケージ: 文字列操作のためのユーティリティ関数を提供します。このコミットでは、strings.Joinが変更前は使用されていましたが、これは文字列スライスを結合する関数です。
  • strconvパッケージ: 文字列と基本的なデータ型(数値、ブール値など)の間での変換機能を提供します。特に、strconv.Quote(s string)関数は、与えられた文字列sをGoの文字列リテラルとして適切にクォートした形式で返します。これは、文字列に特殊文字が含まれる場合に、その文字列を安全に表現するために使用されます。

技術的詳細

このコミットの主要な変更点は、cmd/go/build.goファイルにjoinUnambiguouslyという新しいヘルパー関数が導入されたことです。この関数は、コマンドライン引数のスライス([]string)を受け取り、それらをスペースで結合して1つの文字列として返しますが、その際に必要に応じて個々の引数をクォートします。

joinUnambiguously関数のロジックは以下の通りです。

  1. bytes.Bufferを使用して、効率的に結果文字列を構築します。
  2. 引数のスライスaをループ処理します。
  3. 各引数sについて、まずstrconv.Quote(s)を呼び出して、その引数をGoの文字列リテラルとしてクォートした形式qを取得します。
  4. 以下のいずれかの条件が真である場合、元の引数sの代わりにクォートされた形式qを使用します。
    • s == "" (引数が空文字列の場合)
    • strings.Contains(s, " ") (引数にスペースが含まれる場合)
    • len(q) > len(s)+2 (クォートされた文字列qの長さが、元の文字列sの長さより2文字以上長い場合。これは、strconv.Quoteがエスケープ処理を行った結果、クォート文字(")以外の文字が追加されたことを意味し、例えば改行文字などが含まれる場合に発生します。このような場合も、クォートすることで曖昧さを排除します。)
  5. 上記の条件に当てはまらない場合は、元の引数sをそのまま使用します。
  6. 各引数の間にスペースを挿入して結合します。

このロジックにより、空の引数やスペースを含む引数が常にクォートされ、デバッグ出力の曖昧さが解消されます。また、strconv.Quoteがエスケープ処理を行うような特殊なケースでも、出力が明確になるように配慮されています。

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

diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 2b35136081..9b90728a72 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1237,7 +1237,7 @@ func (b *builder) processOutput(out []byte) string {
 func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) {
  	cmdline := stringList(cmdargs...)\n \tif buildN || buildX {\n-\t\tb.showcmd(dir, "%s", strings.Join(cmdline, " "))\n+\t\tb.showcmd(dir, "%s", joinUnambiguously(cmdline))\n \t\tif buildN {\n \t\t\treturn nil, nil\n \t\t}\n@@ -1304,6 +1304,24 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter\n \t}\n }\n \n+// joinUnambiguously prints the slice, quoting where necessary to make the\n+// output unambiguous.\n+func joinUnambiguously(a []string) string {\n+\tvar buf bytes.Buffer\n+\tfor i, s := range a {\n+\t\tif i > 0 {\n+\t\t\tbuf.WriteByte(' ')\n+\t\t}\n+\t\tq := strconv.Quote(s) // 引数sをGoの文字列リテラルとしてクォートした形式を取得\n+\t\tif s == "" || strings.Contains(s, " ") || len(q) > len(s)+2 {\n+\t\t\tbuf.WriteString(q)\n+\t\t} else {\n+\t\t\tbuf.WriteString(s)\n+\t\t}\n+\t}\n+\treturn buf.String()\n+}\n+\n // mkdir makes the named directory.\n func (b *builder) mkdir(dir string) error {\n \tb.exec.Lock()\n```

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

### `func (b *builder) runOut(...)` の変更

`runOut`関数は、Goツールチェインが外部コマンドを実行する際に使用される内部関数です。この関数内で、デバッグ出力(`buildN`または`buildX`フラグが有効な場合)のためにコマンドライン引数を文字列として結合する部分が変更されました。

変更前:
```go
b.showcmd(dir, "%s", strings.Join(cmdline, " "))

ここでは、strings.Joinを使用して、引数スライスcmdlineをスペースで単純に結合していました。この方法では、空の引数やスペースを含む引数が適切にクォートされず、出力が曖昧になる可能性がありました。

変更後:

b.showcmd(dir, "%s", joinUnambiguously(cmdline))

新しく導入されたjoinUnambiguously関数が呼び出されるようになりました。これにより、引数が結合される前に、必要に応じてクォート処理が施され、デバッグ出力の可読性が向上します。

func joinUnambiguously(a []string) string の追加

この新しい関数は、引数のスライスaを受け取り、それらを結合して曖昧さのない文字列を生成します。

func joinUnambiguously(a []string) string {
	var buf bytes.Buffer // 効率的な文字列構築のためにbytes.Bufferを使用
	for i, s := range a {
		if i > 0 {
			buf.WriteByte(' ') // 最初の要素以外はスペースを追加
		}
		q := strconv.Quote(s) // 引数sをGoの文字列リテラルとしてクォートした形式を取得
		// 以下のいずれかの条件が真の場合、クォートされた形式qを使用
		// 1. 引数が空文字列の場合 (例: "")
		// 2. 引数にスペースが含まれる場合 (例: "hello world")
		// 3. クォートされた文字列の長さが元の文字列+2 (両端のクォート)よりも長い場合
		//    これは、strconv.Quoteがエスケープ処理を行ったことを意味し、
		//    例えば改行文字などが含まれる場合に発生する。
		if s == "" || strings.Contains(s, " ") || len(q) > len(s)+2 {
			buf.WriteString(q)
		} else {
			buf.WriteString(s) // それ以外の場合は元の文字列をそのまま使用
		}
	}
	return buf.String() // 構築された文字列を返す
}

この関数は、各引数を個別に評価し、その内容に基づいてクォートするかどうかを決定します。これにより、デバッグ出力がより正確で理解しやすいものになります。

関連リンク

参考にした情報源リンク

  • Go言語のgo testコマンドに関する公式ドキュメントやブログ記事
  • Go言語のstringsパッケージとstrconvパッケージの公式ドキュメント
  • シェルにおけるコマンドライン引数のクォートに関する一般的な情報
  • go test -x command line arguments quoting - Web search results (Google Search)