いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-152 (複数の *exec.Cmd をパイプストリームでつないでコマンド実行する)

概要

よく忘れるのでメモメモ。

少し前に exec.Command について以下をメモしていました。

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

(*Cmd)には、StdinPipe, StdoutPipe, StderrPipeというメソッドがあり、そこから標準入力、標準出力、標準エラー出力へのパイプストリームが取得できます。

これを使うとシェルでパイプを繋ぐように*exec.Cmdをつなぐことが出来ます。

サンプル

package main

import (
    "fmt"
    "io"
    "os"
    "os/exec"
)

func main() {
    var (
        exitCode int 
        err      error
    )   

    if err = run(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        exitCode = 1 
    }   

    os.Exit(exitCode)
}


func run() error {
    var (
        cmds = []*exec.Cmd{
            exec.Command("ls", "-1", "-a", homeDir()),
            exec.Command("grep", "-v", "-E", "^[.].*"),
            exec.Command("wc", "-l"),
        }   
        err error
    )

    if err = prepare(cmds); err != nil {
        return err
    }

    if err = build(cmds); err != nil {
        return err
    }

    if err = start(cmds); err != nil {
        return err
    }

    if err = wait(cmds); err != nil {
        return err
    }

    return nil
}


func homeDir() string {
    var (
        dir     string
        err     error
    )

    if dir, err = os.UserHomeDir(); err != nil {
        panic(err)
    }

    return dir
}

func prepare(cmds []*exec.Cmd) error {
    cmds[0].Stdin = os.Stdin
    cmds[len(cmds)-1].Stdout = os.Stdout

    return nil
}

func build(cmds []*exec.Cmd) error {
    for i := 0; i < len(cmds)-1; i++ {
        var (
            curr = cmds[i]
            next = cmds[i+1]
            out  io.ReadCloser
            err  error
        )

        if out, err = curr.StdoutPipe(); err != nil {
            return err
        }

        next.Stdin = out
    }

    return nil
}

func start(cmds []*exec.Cmd) error {
    for _, c := range cmds {
        if err := c.Start(); err != nil {
            return err
        }
    }

    return nil
}

func wait(cmds []*exec.Cmd) error {
    for _, c := range cmds {
        if err := c.Wait(); err != nil {
            return err
        }
    }

    return nil
}

参考資料

pkg.go.dev

golang.org


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com