Edited at

【golang】外部コマンドを叩き、標準出力・エラー出力をリアルタイムで拾う

More than 1 year has passed since last update.


なにこれ

goでプロセス(外部コマンド)を起動し、そのプロセスの標準出力・エラー出力を

リアルタイムで追いかけるという事をします。goをバックエンドにプロセスを実行し、

その結果を利用して何かするってときに使えるかもしれません。


仕組み


  • exec.Commandで指定したプロセスのstdout, stderrをgoroutineでさばく

  • goroutineで拾ったデータをchannel経由で親に投げる


プログラム


package main

import (
"bufio"
"log"
"os/exec"
)

func main() {

// コマンドと引数を定義する
c := "go"
p := []string{"run", "main.go"}
cmd := exec.Command(c, p...)

// 実行ディレクトリ
cmd.Dir = "../test"

// パイプを作る
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}

err = cmd.Start()
if err != nil {
log.Fatal(err)
}

streamReader := func(scanner *bufio.Scanner, outputChan chan string, doneChan chan bool) {
defer close(outputChan)
defer close(doneChan)
for scanner.Scan() {
outputChan <- scanner.Text()
}
doneChan <- true
}

// stdout, stderrをひろうgoroutineを起動
stdoutScanner := bufio.NewScanner(stdout)
stdoutOutputChan := make(chan string)
stdoutDoneChan := make(chan bool)
stderrScanner := bufio.NewScanner(stderr)
stderrOutputChan := make(chan string)
stderrDoneChan := make(chan bool)
go streamReader(stdoutScanner, stdoutOutputChan, stdoutDoneChan)
go streamReader(stderrScanner, stderrOutputChan, stderrDoneChan)

// channel経由でデータを引っこ抜く
stillGoing := true
for stillGoing {
select {
case <-stdoutDoneChan:
stillGoing = false
case line := <-stdoutOutputChan:
log.Println(line)
case line := <-stderrOutputChan:
log.Println(line)
}
}

//一応Waitでプロセスの終了をまつ
ret := cmd.Wait()
if ret != nil {
log.Fatal(err)
}
}


検証用スクリプト

以下のスクリプトは、1秒おきにstdout, stderrに時刻を投げるものである。

これを使って、上記スクリプトがに標準出力・エラー出力を拾えているか確認する。

package main

import (
"fmt"
"os"
"time"
)

func main() {
ticker := time.NewTicker(1 * time.Second)
stop := make(chan bool)
go func() {
loop:
for {
select {
case t := <-ticker.C:
fmt.Fprintln(os.Stdout, "(STDOUT)Tick at", t)
fmt.Fprintln(os.Stderr, "(STDERR)Tick at", t)
case <-stop:
break loop
}
}
fmt.Println("Reachable!")
}()

time.Sleep(10 * time.Second)
ticker.Stop()
close(stop)
}

上記スクリプトの実行結果。


vagrant@main:~/test$ go run main.go
(STDOUT)Tick at 2017-01-09 11:31:59.881743758 +0000 UTC
(STDERR)Tick at 2017-01-09 11:31:59.881743758 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:00.888469008 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:00.888469008 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:01.87860581 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:01.87860581 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:02.878606943 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:02.878606943 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:03.878626462 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:03.878626462 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:04.878601052 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:04.878601052 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:05.878555855 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:05.878555855 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:06.878538934 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:06.878538934 +0000 UTC
(STDOUT)Tick at 2017-01-09 11:32:07.882955526 +0000 UTC
(STDERR)Tick at 2017-01-09 11:32:07.882955526 +0000 UTC


検証

検証用スクリプトで出力されるstdout, stderrが拾えている事が確認できる。

(Log.Printlnを使っているので、時刻がつく)

vagrant@main:~/main$ go run main.go

2017/01/09 11:32:12 (STDOUT)Tick at 2017-01-09 11:32:12.03355078 +0000 UTC
2017/01/09 11:32:12 (STDERR)Tick at 2017-01-09 11:32:12.03355078 +0000 UTC
2017/01/09 11:32:13 (STDOUT)Tick at 2017-01-09 11:32:13.033572654 +0000 UTC
2017/01/09 11:32:13 (STDERR)Tick at 2017-01-09 11:32:13.033572654 +0000 UTC
2017/01/09 11:32:14 (STDOUT)Tick at 2017-01-09 11:32:14.034600479 +0000 UTC
2017/01/09 11:32:14 (STDERR)Tick at 2017-01-09 11:32:14.034600479 +0000 UTC
2017/01/09 11:32:15 (STDOUT)Tick at 2017-01-09 11:32:15.034931819 +0000 UTC
2017/01/09 11:32:15 (STDERR)Tick at 2017-01-09 11:32:15.034931819 +0000 UTC
2017/01/09 11:32:16 (STDOUT)Tick at 2017-01-09 11:32:16.034094719 +0000 UTC
2017/01/09 11:32:16 (STDERR)Tick at 2017-01-09 11:32:16.034094719 +0000 UTC
2017/01/09 11:32:17 (STDOUT)Tick at 2017-01-09 11:32:17.034009404 +0000 UTC
2017/01/09 11:32:17 (STDERR)Tick at 2017-01-09 11:32:17.034009404 +0000 UTC
2017/01/09 11:32:18 (STDOUT)Tick at 2017-01-09 11:32:18.033506672 +0000 UTC
2017/01/09 11:32:18 (STDERR)Tick at 2017-01-09 11:32:18.033506672 +0000 UTC
2017/01/09 11:32:19 (STDOUT)Tick at 2017-01-09 11:32:19.033497518 +0000 UTC
2017/01/09 11:32:19 (STDERR)Tick at 2017-01-09 11:32:19.033497518 +0000 UTC
2017/01/09 11:32:20 (STDOUT)Tick at 2017-01-09 11:32:20.033493399 +0000 UTC
2017/01/09 11:32:20 (STDERR)Tick at 2017-01-09 11:32:20.033493399 +0000 UTC
2017/01/09 11:32:21 (STDOUT)Tick at 2017-01-09 11:32:21.033498948 +0000 UTC
2017/01/09 11:32:21 (STDERR)Tick at 2017-01-09 11:32:21.033498948 +0000 UTC

おしまい