Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

基本

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

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

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

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

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

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

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

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

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

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

コマンドの完了を待つ

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

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()を使うとコマンドに標準入力を渡せます。

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の引数として渡します。

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

ただし、上記の方法だと環境変数まわりで事故る可能性があるので、安全にやりたい方はgo-pipeline使うのがおすすめです。

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

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

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

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は、CmdProcess.Pidで参照できます。

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

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

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

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
tanksuzuki
インフラ屋だったりバックエンド屋だったりフロント屋だったり、色々です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした