Goの子プロセスのstdoutパイプをリダイレクトする


105

プログラムのようなサーバーを実行するプログラムをGoで作成しています(これもGoです)。ここで、親プログラムを開始したターミナルウィンドウに子プログラムのstdoutを配置します。これを行う1つの方法はcmd.Output()関数を使用することですが、これはプロセスが終了した後にのみstdoutを出力します。(このサーバーのようなプログラムは長時間実行され、ログ出力を読みたいので、これは問題です)

変数outはでtype io.ReadCloserあり、自分のタスクを達成するためにそれをどうすればよいかわかりません。また、このトピックに関するWebで役立つものを見つけることができません。

func main() {
    cmd := exec.Command("/path/to/my/child/program")
    out, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println(err)
    }
    err = cmd.Start()
    if err != nil {
        fmt.Println(err)
    }
    //fmt.Println(out)
    cmd.Wait()
} 

コードの説明:Println関数のコメントを外してコードをコンパイルしPrintln(out io.ReadCloser)ます。これは意味のある関数ではないことを知っています。
(出力を生成します&{3 |0 <nil> 0})これらの2行は、コードをコンパイルするためだけに必要です。


1
importステートメントの「exec」行は「os / exec」である必要があります。
evilspacepirate 2013年

情報をありがとう、実際にはexec pre go1のみでしたが、現在はosになっています。go1の更新
mbert

1
実際io.Copyにgoルーチン内で呼び出す必要があるとは思いません
rmonjo 2013

呼び出しcmd.Wait()for{}ループが必要だとは思わない...なぜここにあるのか?
weberc2 2014年

このための@ weberc2は、elimisteveの答えを調べます。プログラムを1回だけ実行する場合は、forループは必要ありません。しかし、cmd.Wait()を呼び出さないと、呼び出されたプログラムが完了する前にmain()が終了し、必要な出力が得られない場合があります
mbert

回答:


207

ここで、親プログラムを開始したターミナルウィンドウに子プログラムのstdoutを配置します。

パイプやゴルーチンをいじる必要はありません。これは簡単です。

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
}

4
さらに、コマンドが入力をリッスンするようにしたい場合は、単に設定cmd.Stdin = os.Stdinして、シェルからそのコマンドを文字通り実行したかのように設定できます。
Nucleon 2014年

4
logstdoutの代わりにリダイレクトしたい人のために、ここに
Rick Smith、

18

これをインポートioosて置き換えると、

//fmt.Println(out)

これとともに:

go io.Copy(os.Stdout, out)

(ドキュメントの参照io.Copyのためにos.Stdout、それはあなたがやりたいだろう)。(免責事項:テストされていません。)

ちなみに、標準出力の場合と同じアプローチを使用して標準エラーをキャプチャすることもできますが、cmd.StderrPipeおよびを使用しos.Stderrます。


2
@mbert:私は他の言語を十分に使用し、Goについて十分に読んで、これを行うためにどのような機能が存在する可能性があり、おおよそどのような形で存在するかについての洞察を得ました。次に、関連するpackage-docs(グーグルが見つけた)を調べて、私の直感が正しいことを確認し、必要な詳細を見つける必要がありました。最も難しい部分は、(1)標準出力の名前を見つけること(os.Stdout)と(2)まったく呼び出さない場合cmd.StdoutPipe()、標準出力が/dev/null親プロセスの標準出力ではなくに行くという前提を確認することでした。。
ruakh 2012年

15

ループでこれを必要としないが、cmd.Wait()他のステートメントをブロックせずにコマンド出力をターミナルにエコーしたい場合:

package main

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

func checkError(err error) {
    if err != nil {
        log.Fatalf("Error: %s", err)
    }
}

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")

    // Create stdout, stderr streams of type io.Reader
    stdout, err := cmd.StdoutPipe()
    checkError(err)
    stderr, err := cmd.StderrPipe()
    checkError(err)

    // Start command
    err = cmd.Start()
    checkError(err)

    // Don't let main() exit before our command has finished running
    defer cmd.Wait()  // Doesn't block

    // Non-blockingly echo command output to terminal
    go io.Copy(os.Stdout, stdout)
    go io.Copy(os.Stderr, stderr)

    // I love Go's trivial concurrency :-D
    fmt.Printf("Do other stuff here! No need to wait.\n\n")
}

マイナーファイ:(明らかに)「他の操作をここで行う」がゴルーチンよりも速く完了すると、ゴルーチンの結果が見落とされる可能性があります。main()が終了すると、ゴルーチンも終了します。そのため、コマンドが完了するのを待たなければ、端末にエコーを出力することができなくなる可能性があります。
galaktor 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.