Documentation
¶
Overview ¶
execパッケージは外部コマンドを実行します。これはos.StartProcessをラップして、 stdinとstdoutのリマップ、パイプを使用したI/Oの接続、その他の調整を 簡単に行うことができます。
Cや他の言語からの"system"ライブラリ呼び出しとは異なり、 os/execパッケージは意図的にシステムシェルを呼び出さず、 グロブパターンを展開したり、シェルが通常行う他の展開、 パイプライン、リダイレクションを処理しません。このパッケージは Cの"exec"関数群のように振る舞います。グロブパターンを展開するには、 シェルを直接呼び出し、危険な入力をエスケープするか、 path/filepath パッケージのGlob関数を使用します。 環境変数を展開するには、osパッケージのExpandEnvを使用します。
このパッケージの例はUnixシステムを前提としています。 これらはWindowsでは実行できないかもしれませんし、golang.org や godoc.org が使用する Go Playgroundでは実行できません。
Executables in the current directory ¶
関数 Command と LookPath は、ホストオペレーティングシステムの規則に従って、 現在のパスにリストされたディレクトリでプログラムを探します。 オペレーティングシステムは何十年もの間、この検索に現在の ディレクトリを含めてきました。これは時々暗黙的に、時々 デフォルトで明示的にそのように設定されています。 現代の慣行では、現在のディレクトリを含めることは通常予期しないもので、 しばしばセキュリティ問題につながります。
これらのセキュリティ問題を避けるために、Go 1.19から、このパッケージはプログラムを 現在のディレクトリに対する暗黙的または明示的なパスエントリを使用して解決しません。 つまり、LookPath("go")を実行すると、パスがどのように設定されていても、 Unixでは./go、Windowsでは.\go.exeを正常に返すことはありません。 代わりに、通常のパスアルゴリズムがその答えをもたらす場合、 これらの関数はエラーerrを返し、errors.Is(err, ErrDot)を満たします。
例えば、以下の2つのプログラムスニペットを考えてみてください:
path, err := exec.LookPath("prog") if err != nil { log.Fatal(err) } use(path)
そして
cmd := exec.Command("prog") if err := cmd.Run(); err != nil { log.Fatal(err) }
これらは、現在のパスの設定に関係なく、./progや.\prog.exeを見つけて実行することはありません。
常に現在のディレクトリからプログラムを実行したいコードは、"prog"の代わりに"./prog"と指定することで書き換えることができます。
相対パスエントリからの結果を含めることに固執するコードは、代わりに errors.Is チェックを使用してエラーをオーバーライドできます:
path, err := exec.LookPath("prog") if errors.Is(err, exec.ErrDot) { err = nil } if err != nil { log.Fatal(err) } use(path)
そして
cmd := exec.Command("prog") if errors.Is(cmd.Err, exec.ErrDot) { cmd.Err = nil } if err := cmd.Run(); err != nil { log.Fatal(err) }
環境変数GODEBUG=execerrdot=0を設定すると、 ErrDotの生成が完全に無効になり、よりターゲット指向の修正を適用できないプログラムに対して、 一時的にGo 1.19以前の動作が復元されます。 Goの将来のバージョンでは、この変数のサポートが削除される可能性があります。
そのようなオーバーライドを追加する前に、 それを行うことのセキュリティ上の意味を理解しておいてください。 詳細は https://go.dev/blog/path-security を参照してください。
Index ¶
- Variables
- func LookPath(file string) (string, error)
- type Cmd
- func (c *Cmd) CombinedOutput() ([]byte, error)
- func (c *Cmd) Environ() []string
- func (c *Cmd) Output() ([]byte, error)
- func (c *Cmd) Run() error
- func (c *Cmd) Start() error
- func (c *Cmd) StderrPipe() (io.ReadCloser, error)
- func (c *Cmd) StdinPipe() (io.WriteCloser, error)
- func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
- func (c *Cmd) String() string
- func (c *Cmd) Wait() error
- type Error
- type ExitError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrDot = errors.New("cannot run executable found relative to current directory")
ErrDotは、パスの検索が「.」がパスに含まれているために、 現在のディレクトリ内の実行可能ファイルに解決したことを示します。 これは暗黙的または明示的に行われます。詳細はパッケージのドキュメンテーションを参照してください。
このパッケージの関数はErrDotを直接返さないことに注意してください。 コードはerr == ErrDotではなく、errors.Is(err, ErrDot)を使用して、 返されたエラーerrがこの条件によるものかどうかをテストする必要があります。
var ErrNotFound = errors.New("executable file not found in $PATH")
ErrNotFoundは、パスの検索が実行可能なファイルを見つけられなかった場合のエラーです。
var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete")
ErrWaitDelayは、プロセスが成功したステータスコードで終了するが、 コマンドのWaitDelayが期限切れになる前にその出力パイプが閉じられない場合、 Cmd.Wait によって返されます。
Functions ¶
func LookPath ¶
LookPathは、PATH環境変数で指定されたディレクトリ内の実行可能なファイルを検索します。 もしファイルがスラッシュを含んでいる場合、直接試みられ、 PATHは参照されません。 成功すると、結果は絶対パスになります。
Goの古いバージョンでは、LookPathは現在のディレクトリに対する相対パスを返すことができました。 Go 1.19以降では、LookPathはそのパスとともに、errors.Is(err, ErrDot)を満たすエラーを返します。 詳細はパッケージのドキュメンテーションを参照してください。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { path, err := exec.LookPath("fortune") if err != nil { log.Fatal("installing fortune is in your future") } fmt.Printf("fortune is available at %s\n", path) }
Output:
Types ¶
type Cmd ¶
type Cmd struct { // Pathは、実行するコマンドのパスです。 // // これは唯一、ゼロ以外の値に設定しなければならないフィールドです。 // Pathが相対パスの場合、Dirに対して相対的に評価されます。 Path string // Argsはコマンドライン引数を保持します。コマンド自体はArgs[0]として含まれます。 // Argsフィールドが空またはnilの場合、Runは{Path}を使用します。 // // 典型的な使用では、PathとArgsの両方はCommandを呼び出すことで設定されます。 Args []string // Envはプロセスの環境を指定します。 // 各エントリは "key=value" の形式です。 // Envがnilの場合、新しいプロセスは現在のプロセスの // 環境を使用します。 // Envに環境キーの重複が含まれている場合、各重複キーに対してスライス内の // 最後の値のみが使用されます。 // Windowsでは特別なケースとして、SYSTEMROOTは常に追加されます。 // 明示的に空文字列に設定されていない場合は欠落しています。 Env []string // Dirはコマンドの作業ディレクトリを指定します。 // Dirが空文字列の場合、Runは呼び出し元プロセスの現在のディレクトリでコマンドを実行します。 Dir string // Stdinはプロセスの標準入力を指定します。 // // Stdinがnilの場合、プロセスはnullデバイス(os.DevNull)から読み取ります。 // // Stdinが*os.Fileの場合、プロセスの標準入力はそのファイルに直接接続されます。 // // それ以外の場合、コマンドの実行中に別のgoroutineがStdinから読み取り、 // そのデータをパイプ経由でコマンドに送信します。この場合、Waitはgoroutineが // コピーを停止するまで完了しません。これは、Stdinの終わりに達したため(EOFまたは読み取りエラー)、 // パイプへの書き込みがエラーを返したため、または非ゼロのWaitDelayが設定されて期限切れになったためです。 Stdin io.Reader // StdoutとStderrは、プロセスの標準出力とエラーを指定します。 // // どちらかがnilの場合、Runは対応するファイルディスクリプタを // nullデバイス(os.DevNull)に接続します。 // // どちらかが*os.Fileの場合、プロセスからの対応する出力は // そのファイルに直接接続されます。 // // それ以外の場合、コマンドの実行中に別のgoroutineがプロセスからパイプ経由で読み取り、 // そのデータを対応するWriterに送信します。この場合、Waitはgoroutineが // EOFに達するか、エラーに遭遇するか、非ゼロのWaitDelayが期限切れになるまで完了しません。 // // StdoutとStderrが同じWriterで、==で比較できる型を持っている場合、 // 同時に最大1つのgoroutineだけがWriteを呼び出します。 Stdout io.Writer Stderr io.Writer // ExtraFilesは、新しいプロセスに継承される追加のオープンファイルを指定します。 // 標準入力、標準出力、または標準エラーは含まれません。非nilの場合、エントリiは // ファイルディスクリプタ3+iになります。 // // ExtraFilesはWindowsではサポートされていません。 ExtraFiles []*os.File // SysProcAttrは、オプションのオペレーティングシステム固有の属性を保持します。 // Runは、os.ProcAttrのSysフィールドとしてos.StartProcessに渡します。 SysProcAttr *syscall.SysProcAttr // Processは、開始された後の基本的なプロセスです。 Process *os.Process // ProcessStateは、終了したプロセスに関する情報を含みます。 // プロセスが正常に開始された場合、コマンドが完了するとWaitまたはRunが // そのProcessStateを設定します。 ProcessState *os.ProcessState Err error // Cancelがnilでない場合、コマンドはCommandContextで作成されていなければならず、 // コマンドのContextが完了したときにCancelが呼び出されます。デフォルトでは、 // CommandContextはCancelをコマンドのProcessのKillメソッドを呼び出すように設定します。 // // 通常、カスタムCancelはコマンドのProcessにシグナルを送信しますが、 // 代わりにキャンセルを開始するための他のアクションを取ることもあります。 // 例えば、stdinやstdoutのパイプを閉じる、またはネットワークソケットにシャットダウンリクエストを送信するなどです。 // // Cancelが呼び出された後にコマンドが成功ステータスで終了し、 // そしてCancelがos.ErrProcessDoneと等価のエラーを返さない場合、 // Waitや類似のメソッドは非nilのエラーを返します:Cancelによって返されたエラーをラップするエラー、 // またはContextからのエラーです。 // (コマンドが非成功ステータスで終了する場合、またはCancelがos.ErrProcessDoneをラップするエラーを返す場合、 // Waitや類似のメソッドは引き続きコマンドの通常の終了ステータスを返します。) // // Cancelがnilに設定されている場合、コマンドのContextが完了したときにはすぐには何も起こりませんが、 // 非ゼロのWaitDelayは依然として効果を発揮します。これは、例えば、シャットダウンシグナルをサポートしていないが、 // 常にすぐに終了することが期待されるコマンドのデッドロックを回避するために役立つかもしれません。 // // Startが非nilのエラーを返す場合、Cancelは呼び出されません。 Cancel func() error // WaitDelayが非ゼロの場合、Waitで予期しない遅延の2つの源に対する待機時間を制限します: // 関連するContextがキャンセルされた後も終了しない子プロセス、およびI/Oパイプを閉じずに終了する子プロセス。 // // WaitDelayタイマーは、関連付けられたContextが完了したとき、または // Waitの呼び出しで子プロセスが終了したことが確認されたときのいずれか早い方から開始します。 // 遅延が経過すると、コマンドは子プロセスと/またはそのI/Oパイプをシャットダウンします。 // // 子プロセスが終了に失敗した場合 — たとえば、Cancel関数からのシャットダウンシグナルを無視したり、 // 受信に失敗したりした場合、またはCancel関数が設定されていなかった場合 — それはos.Process.Killを使用して終了されます。 // // その後、子プロセスと通信するI/Oパイプがまだ開いている場合、 // それらのパイプは、現在ReadまたはWrite呼び出しでブロックされているgoroutineを解除するために閉じられます。 // // WaitDelayによりパイプが閉じられ、Cancelの呼び出しが行われておらず、 // コマンドがそれ以外の点で成功ステータスで終了した場合、Waitや類似のメソッドは // nilの代わりにErrWaitDelayを返します。 // // WaitDelayがゼロ(デフォルト)の場合、I/OパイプはEOFまで読み取られます。 // これは、コマンドの孤立したサブプロセスもパイプのディスクリプタを閉じるまで発生しないかもしれません。 WaitDelay time.Duration // contains filtered or unexported fields }
Cmdは、準備中または実行中の外部コマンドを表します。
Cmdは、Cmd.Run、Cmd.Output、または Cmd.CombinedOutput メソッドを呼び出した後では再利用できません。
func Command ¶
Commandは、指定されたプログラムを 与えられた引数で実行するための Cmd 構造体を返します。
それは返される構造体の中でPathとArgsだけを設定します。
nameにパスセパレータが含まれていない場合、Commandは LookPath を使用して 可能な場合にはnameを完全なパスに解決します。それ以外の場合、nameを 直接Pathとして使用します。
返されるCmdのArgsフィールドは、コマンド名に続くargの要素から構築されます。 したがって、argにはコマンド名自体を含めないでください。例えば、Command("echo", "hello")。 Args[0]は常にnameで、解決されたPathではありません。
Windowsでは、プロセスはコマンドライン全体を単一の文字列として受け取り、 自身でパースします。CommandはArgsを結合し、引用符で囲んで、 CommandLineToArgvWを使用するアプリケーションと互換性のあるアルゴリズムで コマンドライン文字列にします(これが最も一般的な方法です)。注目すべき例外は、 msiexec.exeとcmd.exe(したがって、すべてのバッチファイル)で、これらは異なる アンクォートアルゴリズムを持っています。これらまたは他の類似のケースでは、 自分で引用符を付けてSysProcAttr.CmdLineに完全なコマンドラインを提供し、 Argsを空にすることができます。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" "github.com/shogo82148/std/strings" ) func main() { cmd := exec.Command("tr", "a-z", "A-Z") cmd.Stdin = strings.NewReader("some input") var out strings.Builder cmd.Stdout = &out err := cmd.Run() if err != nil { log.Fatal(err) } fmt.Printf("in all caps: %q\n", out.String()) }
Output:
Example (Environment) ¶
package main import ( "github.com/shogo82148/std/log" "github.com/shogo82148/std/os" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("prog") cmd.Env = append(os.Environ(), "FOO=duplicate_value", // 無視されます "FOO=actual_value", // この値が使用されます ) if err := cmd.Run(); err != nil { log.Fatal(err) } }
Output:
func CommandContext ¶ added in v1.7.0
CommandContextは Command と同様ですが、contextが含まれています。
提供されたcontextは、コマンドが自身で完了する前にcontextがdoneになった場合、 プロセスを中断するために使用されます(cmd.Cancelまたは os.Process.Kill を呼び出す)。
CommandContextは、コマンドのCancel関数をそのProcessのKillメソッドを呼び出すように設定し、 WaitDelayは未設定のままにします。呼び出し元は、コマンドを開始する前にこれらのフィールドを 変更することでキャンセルの振る舞いを変更することができます。
Example ¶
package main import ( "github.com/shogo82148/std/context" "github.com/shogo82148/std/os/exec" "github.com/shogo82148/std/time" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil { // これは100ミリ秒後に失敗します。5秒間のスリープは // 中断されます。 } }
Output:
func (*Cmd) CombinedOutput ¶
CombinedOutputはコマンドを実行し、その標準出力と標準エラーを結合したものを返します。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr") stdoutStderr, err := cmd.CombinedOutput() if err != nil { log.Fatal(err) } fmt.Printf("%s\n", stdoutStderr) }
Output:
func (*Cmd) Environ ¶ added in v1.19.0
Environは、現在設定されている状態でコマンドが実行される環境のコピーを返します。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("pwd") // Set Dir before calling cmd.Environ so that it will include an // updated PWD variable (on platforms where that is used). cmd.Dir = ".." cmd.Env = append(cmd.Environ(), "POSIXLY_CORRECT=1") out, err := cmd.Output() if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output:
func (*Cmd) Output ¶
Outputはコマンドを実行し、その標準出力を返します。 返されるエラーは通常、*ExitError 型です。 c.Stderrがnilだった場合、Outputは [ExitError.Stderr] を設定します。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { out, err := exec.Command("date").Output() if err != nil { log.Fatal(err) } fmt.Printf("The date is %s\n", out) }
Output:
func (*Cmd) Run ¶
Runは指定されたコマンドを開始し、その完了を待ちます。
返されるエラーは、コマンドが実行され、stdin、stdout、stderrのコピーに問題がなく、 ゼロの終了ステータスで終了した場合にはnilです。
コマンドが開始されるが正常に完了しない場合、エラーは *ExitError 型です。他の状況では他のエラータイプが返される可能性があります。
呼び出し元のgoroutineが runtime.LockOSThread でオペレーティングシステムのスレッドをロックし、 継承可能なOSレベルのスレッド状態(例えば、LinuxやPlan 9の名前空間)を変更した場合、 新しいプロセスは呼び出し元のスレッド状態を継承します。
Example ¶
package main import ( "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("sleep", "1") log.Printf("Running command and waiting for it to finish...") err := cmd.Run() log.Printf("Command finished with error: %v", err) }
Output:
func (*Cmd) Start ¶
Startは指定されたコマンドを開始しますが、その完了を待ちません。
Startが成功すると、c.Processフィールドが設定されます。
Startの成功した呼び出しの後、関連するシステムリソースを解放するために Cmd.Wait メソッドを呼び出す必要があります。
Example ¶
package main import ( "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("sleep", "5") err := cmd.Start() if err != nil { log.Fatal(err) } log.Printf("Waiting for command to finish...") err = cmd.Wait() log.Printf("Command finished with error: %v", err) }
Output:
func (*Cmd) StderrPipe ¶
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipeは、コマンドが開始されたときにコマンドの標準エラーに接続されるパイプを返します。
Cmd.Wait は、コマンドの終了を確認した後にパイプを閉じるため、 ほとんどの呼び出し元は自分でパイプを閉じる必要はありません。 したがって、パイプからのすべての読み取りが完了する前にWaitを呼び出すことは誤りです。 同様の理由で、StderrPipeを使用しているときに Cmd.Run を呼び出すことも誤りです。 一般的な使用法については、例を参照してください。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/io" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr") stderr, err := cmd.StderrPipe() if err != nil { log.Fatal(err) } if err := cmd.Start(); err != nil { log.Fatal(err) } slurp, _ := io.ReadAll(stderr) fmt.Printf("%s\n", slurp) if err := cmd.Wait(); err != nil { log.Fatal(err) } }
Output:
func (*Cmd) StdinPipe ¶
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipeは、コマンドが開始されたときにコマンドの標準入力に接続されるパイプを返します。 パイプは、Cmd.Wait がコマンドの終了を確認した後、自動的に閉じられます。 呼び出し元は、パイプを早く閉じるためにCloseを呼び出すだけでよいです。 例えば、実行されるコマンドが標準入力が閉じるまで終了しない場合、呼び出し元はパイプを閉じる必要があります。
Example ¶
package main import ( "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/io" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("cat") stdin, err := cmd.StdinPipe() if err != nil { log.Fatal(err) } go func() { defer stdin.Close() io.WriteString(stdin, "values written to stdin are passed to cmd's standard input") }() out, err := cmd.CombinedOutput() if err != nil { log.Fatal(err) } fmt.Printf("%s\n", out) }
Output:
func (*Cmd) StdoutPipe ¶
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipeは、コマンドが開始されたときにコマンドの標準出力に接続されるパイプを返します。
Cmd.Wait は、コマンドの終了を確認した後にパイプを閉じるため、 ほとんどの呼び出し元は自分でパイプを閉じる必要はありません。 したがって、パイプからのすべての読み取りが完了する前にWaitを呼び出すことは誤りです。 同様の理由で、StdoutPipeを使用しているときに Cmd.Run を呼び出すことも誤りです。 一般的な使用法については、例を参照してください。
Example ¶
package main import ( "github.com/shogo82148/std/encoding/json" "github.com/shogo82148/std/fmt" "github.com/shogo82148/std/log" "github.com/shogo82148/std/os/exec" ) func main() { cmd := exec.Command("echo", "-n", `{"Name": "Bob", "Age": 32}`) stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal(err) } if err := cmd.Start(); err != nil { log.Fatal(err) } var person struct { Name string Age int } if err := json.NewDecoder(stdout).Decode(&person); err != nil { log.Fatal(err) } if err := cmd.Wait(); err != nil { log.Fatal(err) } fmt.Printf("%s is %d years old\n", person.Name, person.Age) }
Output:
func (*Cmd) String ¶ added in v1.13.0
Stringは、cの人間が読める説明を返します。 これはデバッグ専用です。 特に、シェルへの入力として使用するのには適していません。 Stringの出力はGoのリリースによって異なる可能性があります。
func (*Cmd) Wait ¶
Waitは、コマンドが終了するのを待ち、stdinへのコピーまたは stdoutまたはstderrからのコピーが完了するのを待ちます。
コマンドは Cmd.Start によって開始されていなければなりません。
返されるエラーは、コマンドが実行され、stdin、stdout、stderrのコピーに問題がなく、 ゼロの終了ステータスで終了した場合にはnilです。
コマンドが実行に失敗するか、正常に完了しない場合、 エラーは *ExitError 型です。I/O問題に対しては他のエラータイプが 返される可能性があります。
c.Stdin、c.Stdout、c.Stderrのいずれかが *os.File でない場合、 Waitは、プロセスへのまたはプロセスからの対応するI/Oループのコピーが 完了するのを待ちます。
Waitは、Cmdに関連付けられたリソースを解放します。
type ExitError ¶
type ExitError struct { *os.ProcessState // Stderrは、標準エラーが他の方法で収集されていない場合、 // Cmd.Outputメソッドからの標準エラー出力の一部を保持します。 // // エラー出力が長い場合、Stderrは出力のプレフィックスと // サフィックスのみを含む可能性があり、中間部分は省略された // バイト数に関するテキストに置き換えられます。 // // Stderrはデバッグ用に提供され、エラーメッセージに含めるためです。 // 他のニーズを持つユーザーは、必要に応じてCmd.Stderrをリダイレクトしてください。 Stderr []byte }
ExitErrorは、コマンドによる成功しない終了を報告します。