Golangで外部コマンドを実行する方法まとめ

Golangは`os/exec`パッケージを使用して外部コマンドを実行できます。

# 基本

処理したいコマンドを`exec.Command()`に渡します。
第一引数がコマンド、第二引数以降(可変長)はコマンドオプションです。
[https://golang.org/pkg/os/exec/#Command](https://golang.org/pkg/os/exec/#Command)

# コマンドを実行する(結果を取得しない)

単に実行するだけであれば、`.Run()`を使います。
`.Run()`の場合、コマンドの実行が完了するまで待ちます。

```go
err := exec.Command("ls", "-la").Run()
```

# コマンドを実行して結果を取得する

実行結果も取得したい場合は`.Output()`を使います。
こちらも`.Run()`同様、コマンドの完了を待ちます。

```go
out, err := exec.Command("ls", "-la").Output()
```

# コマンドを実行するが、完了を待たない

完了を待たなくて良い場合は、`.Start()`を使います。
`.Start()`で開始したコマンドの終了を待ちたい時は、後述の`.Wait()`を使います。

```go
err := exec.Command("nc", "-l").Start()
```

# コマンドの完了を待つ

`.Start()`で開始したコマンドの終了を待つ場合、`.Wait()`を使います。
下記は`sleep 5s`を`.Start()`した後に`.Wait()`するサンプルです。

```go
package main

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

func main() {
	fmt.Println("処理開始: ", time.Now().Format("15:04:05"))
	cmd := exec.Command("sleep", "5s")
	cmd.Start()
	fmt.Println("sleep中: ", time.Now().Format("15:04:05"))
	cmd.Wait()
	fmt.Println("sleep終了: ", time.Now().Format("15:04:05"))
}
```

実行結果

```
処理開始:  19:23:16
sleep中:  19:23:16
sleep終了:  19:23:21
```

`.Wait()`で5秒待ってくれてますね。

# 標準入力を渡す

`.StdinPipe()`を使うとコマンドに標準入力を渡せます。

```go
package main

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

func main() {
    cmd := exec.Command("wc")
    stdin, _ := cmd.StdinPipe()
    io.WriteString(stdin, "hoge")
    stdin.Close()
    out, _ := cmd.Output()
    fmt.Printf("結果: %s", out)
}
```

実行結果

```
結果:        0       1       4
```


# パイプやリダイレクトを使う

パイプやリダイレクトは直接`exec.Command()`に渡せないので、`sh -c`の引数として渡します。

```go
cmdstr := "ip route | grep default"
out, err := exec.Command("sh", "-c", cmdstr).Output()
```

ただし、上記の方法だと環境変数まわりで事故る可能性があるので、安全にやりたい方は[go-pipeline](https://github.com/mattn/go-pipeline)使うのがおすすめです。

# コマンド文字列を`exec.Command`の引数として渡したい

コマンドとオプションが一緒に格納されている文字列を`exec.Command()`に渡したい場合、mattnさん作の[go-shellwords](https://github.com/mattn/go-shellwords)を使うと便利です。

下記の関数は、コマンドとオプションが入った文字列を[go-shellwords](https://github.com/mattn/go-shellwords)でパース（スライス化）して`exec.Command().Run()`を呼び出すサンプルです。

```go
func runCmdStr(cmdstr string) error {
	// 文字列をコマンド、オプション単位でスライス化する
	c, err := shellwords.Parse(cmdstr)
	if err != nil {
		return err
	}
	switch len(c) {
	case 0:
		// 空の文字列が渡された場合
		return nil
	case 1:
		// コマンドのみを渡された場合
		err = exec.Command(c[0]).Run()
	default:
		// コマンド+オプションを渡された場合
		// オプションは可変長でexec.Commandに渡す
		err = exec.Command(c[0], c[1:]...).Run()
	}
	if err != nil {
		return err
	}
	return nil
}
```

# 実行している(した)コマンドのPIDを取得する

PIDは、[`Cmd`型](https://golang.org/pkg/os/exec/#Cmd)の[`Process.Pid`](https://golang.org/pkg/os/#Process)で参照できます。

コマンド実行前に参照するとpanic起こすので注意。

また、`Process`配下のメソッドでプロセスをkillしたりシグナル送ったりできます。
参考：[https://golang.org/pkg/os/#Process](https://golang.org/pkg/os/#Process)

```go
package main

import (
	"fmt"
	"os/exec"
)

func main() {
	cmd := exec.Command("bash")
	cmd.Start()
	fmt.Println("実行中: ", cmd.Process.Pid)
	cmd.Process.Kill()
	cmd.Wait()
	fmt.Println("Kill後: ", cmd.Process.Pid)
}
```

実行結果

```
実行中:  19558
Kill後:  19558
```

# 実行ファイルのパスを知りたい

`exec.LookPath`を使うと`which`的なことができます。

https://golang.org/pkg/os/exec/#LookPath

```go
package main

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

func main() {
	path, err := exec.LookPath("go")
	if err != nil {
		log.Fatal("not found")
	}
	fmt.Printf("go is available at %s\n", path)
}
```

実行結果

```
go is available at /usr/local/go/bin/go
```