[インデックス 1755] ファイルの概要
このコミットは、Go言語の初期開発段階におけるexec
パッケージのドキュメンテーション改善とAPIの明確化、およびregexp
パッケージの軽微なコードスタイル変更を目的としています。特に、外部コマンド実行に関するAPIの可読性と理解度を高めることに重点が置かれています。
コミット
commit 8a7eb77880e8db4021b56731243008271d35a1eb
Author: Russ Cox <rsc@golang.org>
Date: Thu Mar 5 13:35:45 2009 -0800
misc doc
R=r
DELTA=50 (28 added, 0 deleted, 22 changed)
OCL=25763
CL=25770
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8a7eb77880e8db4021b56731243008271d35a1eb
元コミット内容
misc doc
R=r
DELTA=50 (28 added, 0 deleted, 22 changed)
OCL=25763
CL=25770
変更の背景
このコミットは、Go言語がまだオープンソース化されて間もない、非常に初期の段階(2009年3月)に行われたものです。当時のGo言語の標準ライブラリは活発に開発されており、APIの名称、ドキュメンテーション、コードスタイルなどが頻繁に調整されていました。
exec
パッケージは、プログラムから外部コマンドを実行するための重要な機能を提供します。初期のAPI設計では、関数名や定数名が必ずしも直感的でなかったり、詳細な説明が不足していたりする場合があります。このコミットの背景には、exec
パッケージの利用者がより簡単に、かつ正確に外部コマンドを扱えるように、APIの名称をより適切にし、詳細なドキュメンテーションを追加することで、パッケージの使いやすさと堅牢性を向上させるという意図があります。
また、regexp
パッケージにおけるエラー変数の宣言形式の変更は、コードベース全体の統一されたスタイルを確立しようとする初期の取り組みの一環と考えられます。複数の関連する変数をvar (...)
ブロックでグループ化することは、Go言語における一般的な慣習であり、コードの整理と可読性向上に寄与します。
前提知識の解説
Go言語のパッケージとドキュメンテーション
Go言語では、コードは「パッケージ」という単位で整理されます。各パッケージは特定の機能を提供し、その機能はエクスポートされた(大文字で始まる)関数、型、変数などによって外部に公開されます。Goのツールチェインにはgo doc
というコマンドがあり、ソースコード内のコメントから自動的にドキュメンテーションを生成する機能があります。そのため、Goのコードでは、エクスポートされた要素に対して詳細なコメントを記述することが推奨されます。これらのコメントは、パッケージの目的、関数や型の振る舞い、引数、戻り値などを説明し、利用者がAPIを理解する上で不可欠な情報となります。
exec
パッケージの役割
exec
パッケージは、Goプログラムからオペレーティングシステム上の外部コマンド(例: /bin/ls
, /usr/bin/git
など)を実行するための機能を提供します。これにより、Goプログラムはシェルスクリプトのように他のプログラムと連携し、その出力を処理したり、入力を提供したりすることができます。
外部コマンドを実行する際には、以下の要素を考慮する必要があります。
- コマンドのパスと引数: 実行するコマンドの絶対パス(またはPATH環境変数で解決可能なコマンド名)と、それに渡す引数。
- 環境変数: 実行されるコマンドに引き継がれる環境変数。
- 標準入出力 (Stdin, Stdout, Stderr): 実行されるコマンドの標準入力、標準出力、標準エラー出力をどのように扱うか。これらは、親プロセス(Goプログラム)のストリームに接続したり、パイプを介してGoプログラム内で読み書きしたり、
/dev/null
にリダイレクトしたりすることができます。 - プロセスの管理: 実行されたコマンドのプロセスID (PID) を取得したり、コマンドの終了を待機したり、終了ステータスを取得したりする機能。
regexp
パッケージの役割
regexp
パッケージは、正規表現を扱うための機能を提供します。正規表現は、文字列のパターンマッチングや検索、置換などに用いられる強力なツールです。正規表現のパース中に発生するエラー(例: 不正な括弧の対応、無効なエスケープシーケンスなど)は、os.Error
(Goの初期バージョンにおけるエラー型)として返されます。
Go言語のエラーハンドリング(初期バージョン)
Go言語の初期バージョンでは、エラーは主にos.Error
インターフェース(後にerror
インターフェースに統合)を実装する型として表現されていました。os.NewError
関数は、文字列から新しいエラーインスタンスを作成するために使用されました。
技術的詳細
exec
パッケージの変更点
-
パッケージコメントの追加:
src/lib/exec.go
の冒頭に// The exec package runs external commands.
というパッケージコメントが追加されました。これはgo doc
コマンドで表示されるパッケージの概要となり、パッケージの目的を簡潔に説明します。 -
定数名の変更:
Passthru
定数がPassThrough
にリネームされました。これは、英語のスペルミス修正と、より一般的な命名規則への準拠を目的としています。PassThrough
は、外部コマンドの標準入出力が親プロセスのものに「そのまま通過」することを意味します。 -
Cmd
構造体のフィールドコメント追加:Cmd
構造体の各フィールド(Stdin
,Stdout
,Stderr
,Pid
)に対して、それぞれが何を表すのかを説明するコメントが追加されました。これにより、Cmd
構造体のインスタンスが持つ情報が明確になります。Stdin *os.FD
: 実行されるコマンドの標準入力に接続されたパイプのファイルディスクリプタ。Stdout *os.FD
: 実行されるコマンドの標準出力に接続されたパイプのファイルディスクリプタ。Stderr *os.FD
: 実行されるコマンドの標準エラー出力に接続されたパイプのファイルディスクリプタ。Pid int
: 実行中のコマンドのオペレーティングシステムプロセスID。
-
OpenCmd
からRun
への関数名変更と詳細なドキュメンテーション:OpenCmd
関数がRun
にリネームされました。Run
という名前は、外部コマンドの「実行」という操作をより直接的に表現しており、APIの意図が明確になります。 さらに、Run
関数には非常に詳細なドキュメンテーションが追加されました。このドキュメンテーションは以下の点を明確にしています。- 目的: バイナリプログラムを引数と環境変数で実行すること。
- 戻り値: 新しい
Cmd
構造体へのポインタ、またはエラー。 stdin
,stdout
,stderr
パラメータの挙動:DevNull
:/dev/null
に接続する(入力を無視、出力を破棄)。PassThrough
: 現在のプロセスの標準ストリームに接続する。Pipe
: オペレーティングシステムのパイプに接続する。これにより、Goプログラムがコマンドの入出力を読み書きできるようになります。MergeWithStdout
: (標準エラー出力のみ)標準出力と同じファイルディスクリプタを使用する。これは、標準エラー出力を標準出力にマージしたい場合に便利です。
Cmd
構造体のフィールドとの関連:Pipe
が指定された場合、対応するCmd
フィールド(Stdin
,Stdout
,Stderr
)がパイプのもう一方の端となり、それ以外の場合はnil
となること。
-
Wait
メソッドのドキュメンテーション追加:Cmd
構造体のWait
メソッドにドキュメンテーションが追加されました。このメソッドは、実行中のコマンドの終了を待機し、os.Waitmsg
とエラーを返します。options
引数についても説明され、os
パッケージの詳細を参照するように促しています。 -
Close
メソッドのドキュメンテーション追加:Cmd
構造体のClose
メソッドにドキュメンテーションが追加されました。このメソッドは、実行中のコマンドがまだ終了していない場合はその終了を待機し、その後、Cmd
構造体のStdin
,Stdout
,Stderr
フィールドに設定されている非nil
のファイルディスクリプタを閉じます。これは、リソースリークを防ぐために重要です。
exec_test.go
の変更点
exec.go
におけるOpenCmd
からRun
への関数名変更に伴い、テストファイルsrc/lib/exec_test.go
内のテスト関数名もTestOpenCmdCat
からTestRunCat
へ、TestOpenCmdEcho
からTestRunEcho
へと変更されました。これにより、テストコードとAPIの整合性が保たれます。
regexp
パッケージの変更点
src/lib/regexp/regexp.go
では、正規表現のパースエラーを表す複数のos.Error
変数の宣言形式が変更されました。
変更前:
var ErrInternal = os.NewError("internal error");
var ErrUnmatchedLpar = os.NewError("unmatched '('");
// ... 他のエラー変数
変更後:
var (
ErrInternal = os.NewError("internal error");
ErrUnmatchedLpar = os.NewError("unmatched '('");
// ... 他のエラー変数
)
これは機能的な変更ではなく、複数の関連する変数をvar (...)
ブロック内にまとめるというGo言語の慣習に合わせたコードスタイルの変更です。これにより、コードの可読性が向上し、関連するエラー変数が一箇所にまとまっていることが視覚的に分かりやすくなります。
コアとなるコードの変更箇所
src/lib/exec.go
--- a/src/lib/exec.go
+++ b/src/lib/exec.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The exec package runs external commands.
package exec
import (
@@ -9,13 +10,19 @@ import (
"syscall";
)
+// Arguments to Run.
const (
DevNull = iota;
- Passthru;
+ PassThrough;
Pipe;
MergeWithStdout;
)
+// A Cmd represents a running command.
+// Stdin, Stdout, and Stderr are file descriptors to pipes
+// connected to the running command's standard input, output, and error,
+// or else nil, depending on the arguments to Run.
+// Pid is the running command's operating system process ID.
type Cmd struct {
Stdin *os.FD;
Stdout *os.FD;
@@ -34,7 +41,7 @@ func modeToFDs(mode, fd int) (*os.FD, *os.FD, *os.Error) {
}
f, err := os.Open("/dev/null", rw, 0);
return f, nil, err;
- case Passthru:
+ case PassThrough:
switch fd {
case 0:
return os.Stdin, nil, nil;
@@ -56,12 +63,22 @@ func modeToFDs(mode, fd int) (*os.FD, *os.FD, *os.Error) {
return nil, nil, os.EINVAL;
}
-// Start command running with pipes possibly
-// connected to stdin, stdout, stderr.
-// TODO(rsc): Should the stdin,stdout,stderr args
-// be [3]int instead?\n-func OpenCmd(argv0 string, argv, envv []string, stdin, stdout, stderr int)\n-\t(p *Cmd, err *os.Error)\n+// Run starts the binary prog running with
+// arguments argv and environment envv.
+// It returns a pointer to a new Cmd representing
+// the command or an error.
+//
+// The parameters stdin, stdout, and stderr
+// specify how to handle standard input, output, and error.
+// The choices are DevNull (connect to /dev/null),
+// PassThrough (connect to the current process's standard stream),
+// Pipe (connect to an operating system pipe), and
+// MergeWithStdout (only for standard error; use the same
+// file descriptor as was used for standard output).
+// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
+// of the returned Cmd is the other end of the pipe.
+// Otherwise the field in Cmd is nil.
+func Run(argv0 string, argv, envv []string, stdin, stdout, stderr int) (p *Cmd, err *os.Error)
{
p = new(Cmd);
var fd [3]*os.FD;
@@ -116,6 +133,12 @@ Error:
return nil, err;
}
+// Wait waits for the running command p,
+// returning the Waitmsg returned by os.Wait and an error.
+// The options are passed through to os.Wait.
+// Setting options to 0 waits for p to exit;
+// other options cause Wait to return for other
+// process events; see package os for details.
func (p *Cmd) Wait(options uint64) (*os.Waitmsg, *os.Error) {
if p.Pid < 0 {
return nil, os.EINVAL;
@@ -127,6 +150,9 @@ func (p *Cmd) Wait(options uint64) (*os.Waitmsg, *os.Error) {
return w, err;
}
+// Close waits for the running command p to exit,
+// if it hasn't already, and then closes the non-nil file descriptors
+// p.Stdin, p.Stdout, and p.Stderr.
func (p *Cmd) Close() *os.Error {
if p.Pid >= 0 {
// Loop on interrupt, but
src/lib/regexp/regexp.go
--- a/src/lib/regexp/regexp.go
+++ b/src/lib/regexp/regexp.go
@@ -31,16 +31,18 @@ import (
var debug = false;
// Error codes returned by failures to parse an expression.
-var ErrInternal = os.NewError("internal error");
-var ErrUnmatchedLpar = os.NewError("unmatched '('");
-var ErrUnmatchedRpar = os.NewError("unmatched ')'");
-var ErrUnmatchedLbkt = os.NewError("unmatched '['");
-var ErrUnmatchedRbkt = os.NewError("unmatched ']'");
-var ErrBadRange = os.NewError("bad range in character class");
-var ErrExtraneousBackslash = os.NewError("extraneous backslash");
-var ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)");
-var ErrBareClosure = os.NewError("closure applies to nothing");
-var ErrBadBackslash = os.NewError("illegal backslash escape");
+var (
+ ErrInternal = os.NewError("internal error");
+ ErrUnmatchedLpar = os.NewError("unmatched '('");
+ ErrUnmatchedRpar = os.NewError("unmatched ')'");
+ ErrUnmatchedLbkt = os.NewError("unmatched '['");
+ ErrUnmatchedRbkt = os.NewError("unmatched ']'");
+ ErrBadRange = os.NewError("bad range in character class");
+ ErrExtraneousBackslash = os.NewError("extraneous backslash");
+ ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)");
+ ErrBareClosure = os.NewError("closure applies to nothing");
+ ErrBadBackslash = os.NewError("illegal backslash escape");
+)
// An instruction executed by the NFA
type instr interface {
コアとなるコードの解説
exec
パッケージの変更解説
このコミットの核となる変更は、exec
パッケージのAPIの明確化とドキュメンテーションの充実です。
-
OpenCmd
からRun
への変更: Go言語の設計哲学の一つに「明確さ」があります。OpenCmd
という名前は、コマンドを「開く」というニュアンスを含んでいますが、実際にコマンドを「実行」し、そのプロセスを管理する機能を持つため、Run
というより直接的な名前に変更されました。これにより、関数の目的がより直感的に理解できるようになりました。 -
Run
関数の詳細なドキュメンテーション: 追加されたドキュメンテーションは、Run
関数の引数であるstdin
,stdout
,stderr
の挙動を詳細に説明しています。特に、DevNull
,PassThrough
,Pipe
,MergeWithStdout
といった定数の意味と、それらがCmd
構造体のStdin
,Stdout
,Stderr
フィールドにどのように影響するかを明確にしています。これは、外部コマンドとの入出力連携が複雑になりがちなため、利用者が適切なモードを選択し、パイプを正しく扱う上で非常に重要です。例えば、Pipe
を指定した場合にのみCmd
構造体の対応するフィールドがnil
ではなくなり、Goプログラムからそのパイプを介して読み書きできることが明記されています。 -
Cmd
構造体、Wait
、Close
メソッドのドキュメンテーション:Cmd
構造体のフィールドや、Wait
、Close
といった重要なメソッドにコメントが追加されたことで、exec
パッケージの全体的な使い方がより明確になりました。Wait
メソッドは、実行中のコマンドの終了を待機し、その終了ステータスを取得するために不可欠です。Close
メソッドは、コマンドの終了を待機し、関連するファイルディスクリプタを閉じることで、システムリソースの適切な解放を保証します。これらのドキュメンテーションは、Goプログラムが外部コマンドを安全かつ効率的に利用するためのガイドラインを提供します。
regexp
パッケージの変更解説
regexp
パッケージにおけるエラー変数の宣言形式の変更は、Go言語のコードスタイルに関する初期の決定を反映しています。複数の関連する変数をvar (...)
ブロックで宣言することは、Goの慣用的なスタイルであり、以下の利点があります。
- 可読性の向上: 関連するエラー変数が一箇所にまとまっているため、コードを読んだときに、どのような種類のエラーが定義されているかを一目で把握しやすくなります。
- コードの整理: ファイルの冒頭や特定のセクションで、関連する定数や変数をグループ化することで、コードベース全体の整理に貢献します。
- 保守性の向上: 新しいエラーを追加する際や、既存のエラーを変更する際に、関連する定義が近くにあるため、変更作業が容易になります。
この変更は機能的な影響は全くありませんが、Go言語のコードベース全体の品質と一貫性を高めるための重要なステップでした。
関連リンク
- Go言語の
os/exec
パッケージの現在のドキュメンテーション: https://pkg.go.dev/os/exec - Go言語の
regexp
パッケージの現在のドキュメンテーション: https://pkg.go.dev/regexp
参考にした情報源リンク
- Go言語の公式ドキュメンテーション (pkg.go.dev)
- Go言語のGitHubリポジトリのコミット履歴
- Go言語の初期の設計に関する議論(Go Mailing Listなど、当時の情報源)
- (注: 2009年当時の具体的な議論スレッドを特定するのは困難ですが、Goの設計原則に関する一般的な情報は現在のドキュメントやブログ記事から推測できます。)
- Go言語のコードスタイルガイドライン(Go Proverbsなど)I have provided the detailed explanation as requested.