Go by Example: プロセスのスポーン

時々、Goプログラムでは、他の非Goプロセスをスポーンする必要があります。

package main
import (
    "fmt"
    "io"
    "os/exec"
)
func main() {

引数や入力を必要とせず、単にstdoutに何かを出力する簡単なコマンドから始めます。exec.Commandヘルパーは、この外部プロセスを表すオブジェクトを作成します。

    dateCmd := exec.Command("date")

Outputメソッドはコマンドを実行し、完了するまで待って、標準出力を収集します。エラーがなければ、dateOutは日付情報を含むバイトを保持します。

    dateOut, err := dateCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> date")
    fmt.Println(string(dateOut))

OutputCommandの他のメソッドは、コマンドを実行する際に問題が発生した場合(例: パスが間違っている場合)、*exec.Errorを返し、コマンドが実行されたがゼロ以外のリターンコードで終了した場合、*exec.ExitErrorを返します。

    _, err = exec.Command("date", "-x").Output()
    if err != nil {
        switch e := err.(type) {
        case *exec.Error:
            fmt.Println("failed executing:", err)
        case *exec.ExitError:
            fmt.Println("command exit rc =", e.ExitCode())
        default:
            panic(err)
        }
    }

次に、stdinで外部プロセスにデータをパイプし、stdoutから結果を収集する、もう少し複雑なケースを見ていきます。

    grepCmd := exec.Command("grep", "hello")

ここでは入出力パイプを明示的に取得して、プロセスを開始し、何らかの入力を書き込み、結果を出力から読み取り、最後にプロセスが終了するのを待ちます。

    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()
    grepCmd.Start()
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()
    grepBytes, _ := io.ReadAll(grepOut)
    grepCmd.Wait()

上記の例ではエラーチェックを省略しましたが、すべてのエラーチェックに対して通常のif err != nilパターンを使用できます。また、StdoutPipeの結果だけを収集していますが、StderrPipeもまったく同じ方法で収集できます。

    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))

コマンドをスポーンするときは、単一のコマンドライン文字列を渡すのではなく、明確に区切られたコマンドと引数配列を提供する必要があることに注意してください。文字列で完全なコマンドをスポーンしたい場合は、bash-cオプションを使用できます

    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> ls -a -l -h")
    fmt.Println(string(lsOut))
}

スポーンされたプログラムは、コマンドラインから直接実行した場合と同じ出力を返します。

$ go run spawning-processes.go 
> date
Thu 05 May 2022 10:10:12 PM PDT

dateには-xフラグがないので、エラーメッセージと非ゼロリターンコードで終了します。

command exited with rc = 1
> grep hello
hello grep
> ls -a -l -h
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go

次の例: プロセスの実行