github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/os/exec/exec.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // execパッケージは外部コマンドを実行します。これはos.StartProcessをラップして、
     6  // stdinとstdoutのリマップ、パイプを使用したI/Oの接続、その他の調整を
     7  // 簡単に行うことができます。
     8  //
     9  // Cや他の言語からの"system"ライブラリ呼び出しとは異なり、
    10  // os/execパッケージは意図的にシステムシェルを呼び出さず、
    11  // グロブパターンを展開したり、シェルが通常行う他の展開、
    12  // パイプライン、リダイレクションを処理しません。このパッケージは
    13  // Cの"exec"関数群のように振る舞います。グロブパターンを展開するには、
    14  // シェルを直接呼び出し、危険な入力をエスケープするか、
    15  // [path/filepath] パッケージのGlob関数を使用します。
    16  // 環境変数を展開するには、osパッケージのExpandEnvを使用します。
    17  //
    18  // このパッケージの例はUnixシステムを前提としています。
    19  // これらはWindowsでは実行できないかもしれませんし、golang.org や godoc.org が使用する
    20  // Go Playgroundでは実行できません。
    21  //
    22  // # Executables in the current directory
    23  //
    24  // 関数 [Command] と [LookPath] は、ホストオペレーティングシステムの規則に従って、
    25  // 現在のパスにリストされたディレクトリでプログラムを探します。
    26  // オペレーティングシステムは何十年もの間、この検索に現在の
    27  // ディレクトリを含めてきました。これは時々暗黙的に、時々
    28  // デフォルトで明示的にそのように設定されています。
    29  // 現代の慣行では、現在のディレクトリを含めることは通常予期しないもので、
    30  // しばしばセキュリティ問題につながります。
    31  //
    32  // これらのセキュリティ問題を避けるために、Go 1.19から、このパッケージはプログラムを
    33  // 現在のディレクトリに対する暗黙的または明示的なパスエントリを使用して解決しません。
    34  // つまり、[LookPath]("go")を実行すると、パスがどのように設定されていても、
    35  // Unixでは./go、Windowsでは.\go.exeを正常に返すことはありません。
    36  // 代わりに、通常のパスアルゴリズムがその答えをもたらす場合、
    37  // これらの関数はエラーerrを返し、[errors.Is](err, [ErrDot])を満たします。
    38  //
    39  // 例えば、以下の2つのプログラムスニペットを考えてみてください:
    40  //
    41  //	path, err := exec.LookPath("prog")
    42  //	if err != nil {
    43  //		log.Fatal(err)
    44  //	}
    45  //	use(path)
    46  //
    47  // そして
    48  //
    49  //	cmd := exec.Command("prog")
    50  //	if err := cmd.Run(); err != nil {
    51  //		log.Fatal(err)
    52  //	}
    53  //
    54  // これらは、現在のパスの設定に関係なく、./progや.\prog.exeを見つけて実行することはありません。
    55  //
    56  // 常に現在のディレクトリからプログラムを実行したいコードは、"prog"の代わりに"./prog"と指定することで書き換えることができます。
    57  //
    58  // 相対パスエントリからの結果を含めることに固執するコードは、代わりに errors.Is チェックを使用してエラーをオーバーライドできます:
    59  //
    60  //	path, err := exec.LookPath("prog")
    61  //	if errors.Is(err, exec.ErrDot) {
    62  //		err = nil
    63  //	}
    64  //	if err != nil {
    65  //		log.Fatal(err)
    66  //	}
    67  //	use(path)
    68  //
    69  // そして
    70  //
    71  //	cmd := exec.Command("prog")
    72  //	if errors.Is(cmd.Err, exec.ErrDot) {
    73  //		cmd.Err = nil
    74  //	}
    75  //	if err := cmd.Run(); err != nil {
    76  //		log.Fatal(err)
    77  //	}
    78  //
    79  // 環境変数GODEBUG=execerrdot=0を設定すると、
    80  // ErrDotの生成が完全に無効になり、よりターゲット指向の修正を適用できないプログラムに対して、
    81  // 一時的にGo 1.19以前の動作が復元されます。
    82  // Goの将来のバージョンでは、この変数のサポートが削除される可能性があります。
    83  //
    84  // そのようなオーバーライドを追加する前に、
    85  // それを行うことのセキュリティ上の意味を理解しておいてください。
    86  // 詳細は https://go.dev/blog/path-security を参照してください。
    87  package exec
    88  
    89  import (
    90  	"github.com/shogo82148/std/context"
    91  	"github.com/shogo82148/std/errors"
    92  	"github.com/shogo82148/std/io"
    93  	"github.com/shogo82148/std/os"
    94  	"github.com/shogo82148/std/syscall"
    95  	"github.com/shogo82148/std/time"
    96  )
    97  
    98  // Errorは、[LookPath] がファイルを実行可能なものとして分類できなかったときに返されます。
    99  type Error struct {
   100  	// Nameは、エラーが発生したファイル名です。
   101  	Name string
   102  	// Errは、基になるエラーです。
   103  	Err error
   104  }
   105  
   106  func (e *Error) Error() string
   107  
   108  func (e *Error) Unwrap() error
   109  
   110  // ErrWaitDelayは、プロセスが成功したステータスコードで終了するが、
   111  // コマンドのWaitDelayが期限切れになる前にその出力パイプが閉じられない場合、
   112  // [Cmd.Wait] によって返されます。
   113  var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete")
   114  
   115  // Cmdは、準備中または実行中の外部コマンドを表します。
   116  //
   117  // Cmdは、[Cmd.Run]、[Cmd.Output]、または [Cmd.CombinedOutput] メソッドを呼び出した後では再利用できません。
   118  type Cmd struct {
   119  	// Pathは、実行するコマンドのパスです。
   120  	//
   121  	// これは唯一、ゼロ以外の値に設定しなければならないフィールドです。
   122  	// Pathが相対パスの場合、Dirに対して相対的に評価されます。
   123  	Path string
   124  
   125  	// Argsはコマンドライン引数を保持します。コマンド自体はArgs[0]として含まれます。
   126  	// Argsフィールドが空またはnilの場合、Runは{Path}を使用します。
   127  	//
   128  	// 典型的な使用では、PathとArgsの両方はCommandを呼び出すことで設定されます。
   129  	Args []string
   130  
   131  	// Envはプロセスの環境を指定します。
   132  	// 各エントリは "key=value" の形式です。
   133  	// Envがnilの場合、新しいプロセスは現在のプロセスの
   134  	// 環境を使用します。
   135  	// Envに環境キーの重複が含まれている場合、各重複キーに対してスライス内の
   136  	// 最後の値のみが使用されます。
   137  	// Windowsでは特別なケースとして、SYSTEMROOTは常に追加されます。
   138  	// 明示的に空文字列に設定されていない場合は欠落しています。
   139  	Env []string
   140  
   141  	// Dirはコマンドの作業ディレクトリを指定します。
   142  	// Dirが空文字列の場合、Runは呼び出し元プロセスの現在のディレクトリでコマンドを実行します。
   143  	Dir string
   144  
   145  	// Stdinはプロセスの標準入力を指定します。
   146  	//
   147  	// Stdinがnilの場合、プロセスはnullデバイス(os.DevNull)から読み取ります。
   148  	//
   149  	// Stdinが*os.Fileの場合、プロセスの標準入力はそのファイルに直接接続されます。
   150  	//
   151  	// それ以外の場合、コマンドの実行中に別のgoroutineがStdinから読み取り、
   152  	// そのデータをパイプ経由でコマンドに送信します。この場合、Waitはgoroutineが
   153  	// コピーを停止するまで完了しません。これは、Stdinの終わりに達したため(EOFまたは読み取りエラー)、
   154  	// パイプへの書き込みがエラーを返したため、または非ゼロのWaitDelayが設定されて期限切れになったためです。
   155  	Stdin io.Reader
   156  
   157  	// StdoutとStderrは、プロセスの標準出力とエラーを指定します。
   158  	//
   159  	// どちらかがnilの場合、Runは対応するファイルディスクリプタを
   160  	// nullデバイス(os.DevNull)に接続します。
   161  	//
   162  	// どちらかが*os.Fileの場合、プロセスからの対応する出力は
   163  	// そのファイルに直接接続されます。
   164  	//
   165  	// それ以外の場合、コマンドの実行中に別のgoroutineがプロセスからパイプ経由で読み取り、
   166  	// そのデータを対応するWriterに送信します。この場合、Waitはgoroutineが
   167  	// EOFに達するか、エラーに遭遇するか、非ゼロのWaitDelayが期限切れになるまで完了しません。
   168  	//
   169  	// StdoutとStderrが同じWriterで、==で比較できる型を持っている場合、
   170  	// 同時に最大1つのgoroutineだけがWriteを呼び出します。
   171  	Stdout io.Writer
   172  	Stderr io.Writer
   173  
   174  	// ExtraFilesは、新しいプロセスに継承される追加のオープンファイルを指定します。
   175  	// 標準入力、標準出力、または標準エラーは含まれません。非nilの場合、エントリiは
   176  	// ファイルディスクリプタ3+iになります。
   177  	//
   178  	// ExtraFilesはWindowsではサポートされていません。
   179  	ExtraFiles []*os.File
   180  
   181  	// SysProcAttrは、オプションのオペレーティングシステム固有の属性を保持します。
   182  	// Runは、os.ProcAttrのSysフィールドとしてos.StartProcessに渡します。
   183  	SysProcAttr *syscall.SysProcAttr
   184  
   185  	// Processは、開始された後の基本的なプロセスです。
   186  	Process *os.Process
   187  
   188  	// ProcessStateは、終了したプロセスに関する情報を含みます。
   189  	// プロセスが正常に開始された場合、コマンドが完了するとWaitまたはRunが
   190  	// そのProcessStateを設定します。
   191  	ProcessState *os.ProcessState
   192  
   193  	// ctx is the context passed to CommandContext, if any.
   194  	ctx context.Context
   195  
   196  	Err error
   197  
   198  	// Cancelがnilでない場合、コマンドはCommandContextで作成されていなければならず、
   199  	// コマンドのContextが完了したときにCancelが呼び出されます。デフォルトでは、
   200  	// CommandContextはCancelをコマンドのProcessのKillメソッドを呼び出すように設定します。
   201  	//
   202  	// 通常、カスタムCancelはコマンドのProcessにシグナルを送信しますが、
   203  	// 代わりにキャンセルを開始するための他のアクションを取ることもあります。
   204  	// 例えば、stdinやstdoutのパイプを閉じる、またはネットワークソケットにシャットダウンリクエストを送信するなどです。
   205  	//
   206  	// Cancelが呼び出された後にコマンドが成功ステータスで終了し、
   207  	// そしてCancelがos.ErrProcessDoneと等価のエラーを返さない場合、
   208  	// Waitや類似のメソッドは非nilのエラーを返します:Cancelによって返されたエラーをラップするエラー、
   209  	// またはContextからのエラーです。
   210  	// (コマンドが非成功ステータスで終了する場合、またはCancelがos.ErrProcessDoneをラップするエラーを返す場合、
   211  	// Waitや類似のメソッドは引き続きコマンドの通常の終了ステータスを返します。)
   212  	//
   213  	// Cancelがnilに設定されている場合、コマンドのContextが完了したときにはすぐには何も起こりませんが、
   214  	// 非ゼロのWaitDelayは依然として効果を発揮します。これは、例えば、シャットダウンシグナルをサポートしていないが、
   215  	// 常にすぐに終了することが期待されるコマンドのデッドロックを回避するために役立つかもしれません。
   216  	//
   217  	// Startが非nilのエラーを返す場合、Cancelは呼び出されません。
   218  	Cancel func() error
   219  
   220  	// WaitDelayが非ゼロの場合、Waitで予期しない遅延の2つの源に対する待機時間を制限します:
   221  	// 関連するContextがキャンセルされた後も終了しない子プロセス、およびI/Oパイプを閉じずに終了する子プロセス。
   222  	//
   223  	// WaitDelayタイマーは、関連付けられたContextが完了したとき、または
   224  	// Waitの呼び出しで子プロセスが終了したことが確認されたときのいずれか早い方から開始します。
   225  	// 遅延が経過すると、コマンドは子プロセスと/またはそのI/Oパイプをシャットダウンします。
   226  	//
   227  	// 子プロセスが終了に失敗した場合 — たとえば、Cancel関数からのシャットダウンシグナルを無視したり、
   228  	// 受信に失敗したりした場合、またはCancel関数が設定されていなかった場合 — それはos.Process.Killを使用して終了されます。
   229  	//
   230  	// その後、子プロセスと通信するI/Oパイプがまだ開いている場合、
   231  	// それらのパイプは、現在ReadまたはWrite呼び出しでブロックされているgoroutineを解除するために閉じられます。
   232  	//
   233  	// WaitDelayによりパイプが閉じられ、Cancelの呼び出しが行われておらず、
   234  	// コマンドがそれ以外の点で成功ステータスで終了した場合、Waitや類似のメソッドは
   235  	// nilの代わりにErrWaitDelayを返します。
   236  	//
   237  	// WaitDelayがゼロ(デフォルト)の場合、I/OパイプはEOFまで読み取られます。
   238  	// これは、コマンドの孤立したサブプロセスもパイプのディスクリプタを閉じるまで発生しないかもしれません。
   239  	WaitDelay time.Duration
   240  
   241  	// childIOFiles holds closers for any of the child process's
   242  	// stdin, stdout, and/or stderr files that were opened by the Cmd itself
   243  	// (not supplied by the caller). These should be closed as soon as they
   244  	// are inherited by the child process.
   245  	childIOFiles []io.Closer
   246  
   247  	// parentIOPipes holds closers for the parent's end of any pipes
   248  	// connected to the child's stdin, stdout, and/or stderr streams
   249  	// that were opened by the Cmd itself (not supplied by the caller).
   250  	// These should be closed after Wait sees the command and copying
   251  	// goroutines exit, or after WaitDelay has expired.
   252  	parentIOPipes []io.Closer
   253  
   254  	// goroutine holds a set of closures to execute to copy data
   255  	// to and/or from the command's I/O pipes.
   256  	goroutine []func() error
   257  
   258  	// If goroutineErr is non-nil, it receives the first error from a copying
   259  	// goroutine once all such goroutines have completed.
   260  	// goroutineErr is set to nil once its error has been received.
   261  	goroutineErr <-chan error
   262  
   263  	// If ctxResult is non-nil, it receives the result of watchCtx exactly once.
   264  	ctxResult <-chan ctxResult
   265  
   266  	// The stack saved when the Command was created, if GODEBUG contains
   267  	// execwait=2. Used for debugging leaks.
   268  	createdByStack []byte
   269  
   270  	// For a security release long ago, we created x/sys/execabs,
   271  	// which manipulated the unexported lookPathErr error field
   272  	// in this struct. For Go 1.19 we exported the field as Err error,
   273  	// above, but we have to keep lookPathErr around for use by
   274  	// old programs building against new toolchains.
   275  	// The String and Start methods look for an error in lookPathErr
   276  	// in preference to Err, to preserve the errors that execabs sets.
   277  	//
   278  	// In general we don't guarantee misuse of reflect like this,
   279  	// but the misuse of reflect was by us, the best of various bad
   280  	// options to fix the security problem, and people depend on
   281  	// those old copies of execabs continuing to work.
   282  	// The result is that we have to leave this variable around for the
   283  	// rest of time, a compatibility scar.
   284  	//
   285  	// See https://go.dev/blog/path-security
   286  	// and https://go.dev/issue/43724 for more context.
   287  	lookPathErr error
   288  }
   289  
   290  // Commandは、指定されたプログラムを
   291  // 与えられた引数で実行するための [Cmd] 構造体を返します。
   292  //
   293  // それは返される構造体の中でPathとArgsだけを設定します。
   294  //
   295  // nameにパスセパレータが含まれていない場合、Commandは [LookPath] を使用して
   296  // 可能な場合にはnameを完全なパスに解決します。それ以外の場合、nameを
   297  // 直接Pathとして使用します。
   298  //
   299  // 返されるCmdのArgsフィールドは、コマンド名に続くargの要素から構築されます。
   300  // したがって、argにはコマンド名自体を含めないでください。例えば、Command("echo", "hello")。
   301  // Args[0]は常にnameで、解決されたPathではありません。
   302  //
   303  // Windowsでは、プロセスはコマンドライン全体を単一の文字列として受け取り、
   304  // 自身でパースします。CommandはArgsを結合し、引用符で囲んで、
   305  // CommandLineToArgvWを使用するアプリケーションと互換性のあるアルゴリズムで
   306  // コマンドライン文字列にします(これが最も一般的な方法です)。注目すべき例外は、
   307  // msiexec.exeとcmd.exe(したがって、すべてのバッチファイル)で、これらは異なる
   308  // アンクォートアルゴリズムを持っています。これらまたは他の類似のケースでは、
   309  // 自分で引用符を付けてSysProcAttr.CmdLineに完全なコマンドラインを提供し、
   310  // Argsを空にすることができます。
   311  func Command(name string, arg ...string) *Cmd
   312  
   313  // CommandContextは [Command] と同様ですが、contextが含まれています。
   314  //
   315  // 提供されたcontextは、コマンドが自身で完了する前にcontextがdoneになった場合、
   316  // プロセスを中断するために使用されます(cmd.Cancelまたは [os.Process.Kill] を呼び出す)。
   317  //
   318  // CommandContextは、コマンドのCancel関数をそのProcessのKillメソッドを呼び出すように設定し、
   319  // WaitDelayは未設定のままにします。呼び出し元は、コマンドを開始する前にこれらのフィールドを
   320  // 変更することでキャンセルの振る舞いを変更することができます。
   321  func CommandContext(ctx context.Context, name string, arg ...string) *Cmd
   322  
   323  // Stringは、cの人間が読める説明を返します。
   324  // これはデバッグ専用です。
   325  // 特に、シェルへの入力として使用するのには適していません。
   326  // Stringの出力はGoのリリースによって異なる可能性があります。
   327  func (c *Cmd) String() string
   328  
   329  // Runは指定されたコマンドを開始し、その完了を待ちます。
   330  //
   331  // 返されるエラーは、コマンドが実行され、stdin、stdout、stderrのコピーに問題がなく、
   332  // ゼロの終了ステータスで終了した場合にはnilです。
   333  //
   334  // コマンドが開始されるが正常に完了しない場合、エラーは
   335  // [*ExitError] 型です。他の状況では他のエラータイプが返される可能性があります。
   336  //
   337  // 呼び出し元のgoroutineが [runtime.LockOSThread] でオペレーティングシステムのスレッドをロックし、
   338  // 継承可能なOSレベルのスレッド状態(例えば、LinuxやPlan 9の名前空間)を変更した場合、
   339  // 新しいプロセスは呼び出し元のスレッド状態を継承します。
   340  func (c *Cmd) Run() error
   341  
   342  // Startは指定されたコマンドを開始しますが、その完了を待ちません。
   343  //
   344  // Startが成功すると、c.Processフィールドが設定されます。
   345  //
   346  // Startの成功した呼び出しの後、関連するシステムリソースを解放するために
   347  // [Cmd.Wait] メソッドを呼び出す必要があります。
   348  func (c *Cmd) Start() error
   349  
   350  // ExitErrorは、コマンドによる成功しない終了を報告します。
   351  type ExitError struct {
   352  	*os.ProcessState
   353  
   354  	// Stderrは、標準エラーが他の方法で収集されていない場合、
   355  	// Cmd.Outputメソッドからの標準エラー出力の一部を保持します。
   356  	//
   357  	// エラー出力が長い場合、Stderrは出力のプレフィックスと
   358  	// サフィックスのみを含む可能性があり、中間部分は省略された
   359  	// バイト数に関するテキストに置き換えられます。
   360  	//
   361  	// Stderrはデバッグ用に提供され、エラーメッセージに含めるためです。
   362  	// 他のニーズを持つユーザーは、必要に応じてCmd.Stderrをリダイレクトしてください。
   363  	Stderr []byte
   364  }
   365  
   366  func (e *ExitError) Error() string
   367  
   368  // Waitは、コマンドが終了するのを待ち、stdinへのコピーまたは
   369  // stdoutまたはstderrからのコピーが完了するのを待ちます。
   370  //
   371  // コマンドは [Cmd.Start] によって開始されていなければなりません。
   372  //
   373  // 返されるエラーは、コマンドが実行され、stdin、stdout、stderrのコピーに問題がなく、
   374  // ゼロの終了ステータスで終了した場合にはnilです。
   375  //
   376  // コマンドが実行に失敗するか、正常に完了しない場合、
   377  // エラーは [*ExitError] 型です。I/O問題に対しては他のエラータイプが
   378  // 返される可能性があります。
   379  //
   380  // c.Stdin、c.Stdout、c.Stderrのいずれかが [*os.File] でない場合、
   381  // Waitは、プロセスへのまたはプロセスからの対応するI/Oループのコピーが
   382  // 完了するのを待ちます。
   383  //
   384  // Waitは、Cmdに関連付けられたリソースを解放します。
   385  func (c *Cmd) Wait() error
   386  
   387  // Outputはコマンドを実行し、その標準出力を返します。
   388  // 返されるエラーは通常、[*ExitError] 型です。
   389  // c.Stderrがnilだった場合、Outputは [ExitError.Stderr] を設定します。
   390  func (c *Cmd) Output() ([]byte, error)
   391  
   392  // CombinedOutputはコマンドを実行し、その標準出力と標準エラーを結合したものを返します。
   393  func (c *Cmd) CombinedOutput() ([]byte, error)
   394  
   395  // StdinPipeは、コマンドが開始されたときにコマンドの標準入力に接続されるパイプを返します。
   396  // パイプは、[Cmd.Wait] がコマンドの終了を確認した後、自動的に閉じられます。
   397  // 呼び出し元は、パイプを早く閉じるためにCloseを呼び出すだけでよいです。
   398  // 例えば、実行されるコマンドが標準入力が閉じるまで終了しない場合、呼び出し元はパイプを閉じる必要があります。
   399  func (c *Cmd) StdinPipe() (io.WriteCloser, error)
   400  
   401  // StdoutPipeは、コマンドが開始されたときにコマンドの標準出力に接続されるパイプを返します。
   402  //
   403  // [Cmd.Wait] は、コマンドの終了を確認した後にパイプを閉じるため、
   404  // ほとんどの呼び出し元は自分でパイプを閉じる必要はありません。
   405  // したがって、パイプからのすべての読み取りが完了する前にWaitを呼び出すことは誤りです。
   406  // 同様の理由で、StdoutPipeを使用しているときに [Cmd.Run] を呼び出すことも誤りです。
   407  // 一般的な使用法については、例を参照してください。
   408  func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
   409  
   410  // StderrPipeは、コマンドが開始されたときにコマンドの標準エラーに接続されるパイプを返します。
   411  //
   412  // [Cmd.Wait] は、コマンドの終了を確認した後にパイプを閉じるため、
   413  // ほとんどの呼び出し元は自分でパイプを閉じる必要はありません。
   414  // したがって、パイプからのすべての読み取りが完了する前にWaitを呼び出すことは誤りです。
   415  // 同様の理由で、StderrPipeを使用しているときに [Cmd.Run] を呼び出すことも誤りです。
   416  // 一般的な使用法については、例を参照してください。
   417  func (c *Cmd) StderrPipe() (io.ReadCloser, error)
   418  
   419  // Environは、現在設定されている状態でコマンドが実行される環境のコピーを返します。
   420  func (c *Cmd) Environ() []string
   421  
   422  // ErrDotは、パスの検索が「.」がパスに含まれているために、
   423  // 現在のディレクトリ内の実行可能ファイルに解決したことを示します。
   424  // これは暗黙的または明示的に行われます。詳細はパッケージのドキュメンテーションを参照してください。
   425  //
   426  // このパッケージの関数はErrDotを直接返さないことに注意してください。
   427  // コードはerr == ErrDotではなく、errors.Is(err, ErrDot)を使用して、
   428  // 返されたエラーerrがこの条件によるものかどうかをテストする必要があります。
   429  var ErrDot = errors.New("cannot run executable found relative to current directory")