Golangで、外部コマンド実行

前回Golangをバージョンアップした時動かなくなったexec周りのメモです。

Golangで何かをやろうとして、実装方法がわからない時、最初に当たるのは、公式のリファレンスです。

これは結構しっかり書いてあるんですが、使用例が書いてないので、どう使うのが正しいのかわからない 時があります。検索しても、新しい言語であるせいか、あまりサンプルコードが見付からないです。 そんな時は、Golangのソースに添付されているテストコードと、実際のソースコードを見ることになります。

最初に実装した時(release.r57.1 8294)も、exec周りのソースコードを見て外部コマンド実行ができるように なったんですが、今回見たら(release.r58 8731)、悲しいくらいexec関連のインターフェースが変わっていたので、 新しいインターフェースでの外部コマンド実行のコードをメモしておきます。

main.go

package main
 
import (
        "os"
        "io"
        "log"
        "exec"
)
 
func RunCommand(command string, args []string, environ []string) {
        cmd := exec.Command(command)
        cmd.Env = environ
        cmd.Args = args
        cmd.Dir = "."
        stdout, err := cmd.StdoutPipe()
        if err != nil {
                log.Fatal("failed to retrieve pipe. %s", err)
                os.Exit(-1)
        }
 
        err = cmd.Start()
        if err != nil {
                log.Fatal("failed to execute external command. %s", err)
                os.Exit(-1)
        }
 
        WriteFileStream(stdout)
}
 
func WriteFileStream(reader io.Reader) {
        var (
                err os.Error
                n int
        )
        buf := make([]byte, 1024)
 
        for {
                if n, err = reader.Read(buf); err != nil {
                        break
                }
                log.Print(string(buf[0:n]))
        }
        if err == os.EOF {
                log.Println("stdout end");
                err = nil
        } else {
                log.Println("ERROR: " + err.String());
        }
}
 
func main() {
        environ := os.Environ()
        command := "/bin/ping"
        args := []string {command, "-c", "5", "localhost"}
        RunCommand(command, args, environ)
}

今回やりたかったのは、バックグラウンドで外部コマンドを実行しつつ、流れてくる 標準出力を取得することでした。cmd.StdoutPipe()で取得したio.Readerから、 WriteFileStreamで、標準出力を受けとり次第、ログ出力してます。 最初、buf変数を要素数を指定せずに定義してたところ、標準出力を取ろうとすると、すぐos.EOFが 返ってきて、取得できなかったのですが、makeで領域を確保する形で定義するように変更したところ、 正しく標準出力を取得できました。

Makefile

default: main.go
        6g main.go
        6l -o runcommand main.6
 
run: default
        ./runcommand

実行結果

$ ./runcommand 
2011/07/11 03:03:27 PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.048 ms
2011/07/11 03:03:28 64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.042 ms
2011/07/11 03:03:29 64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.041 ms
2011/07/11 03:03:30 64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.041 ms
2011/07/11 03:03:31 64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.042 ms
2011/07/11 03:03:31 
2011/07/11 03:03:31 --- localhost ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3996ms
rtt min/avg/max/mdev = 0.041/0.042/0.048/0.008 ms
2011/07/11 03:03:31 stdout end

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.