emahiro's ProgrammingBlog

勉強記録と書評とたまに長めの呟きを書きます

Goでシェルを実行するCLIToolを書く

goで外部コマンド(デフォルトのコマンドやshellスクリプト)を実行するCLIツールを作ったのでその触りをまとめます。
内容は、goでCLIツール書く時の実装方法について。

やったこと

  1. shellスクリプトを外部コマンドとして叩く
  2. goの os/exec パッケージを利用してコマンドをひたすら記述していく

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

ディレクトリ構成

-- 
 |- app.go
 |- test.sh

shellスクリプトを外部コマンドとして叩く

使ったシェルスクリプトはとりあえず Hello を表示するだけ

#!bin/shell
echo "Hello"

goのスクリプトこち

package main

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

func main() {
    // カレントディレクトリを移動
    os.Chdir("./cmds")
    // コマンドを実行 .(ピリオド)は使えずにちゃんと sh コマンドを使う
    out, err := exec.Command("sh", "./test.sh").Output()
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    fmt.Println(string(out))
}

実行結果

$ go run app.go
Hello

できた。

パッケージを利用してコマンドをひたすら記述していく

使用パッケージ: os/exec

外部コマンド実行に際してとく使うインターフェイス

  1. exec.Command(cmd, args).Run() 結果を出力せずに実行。戻り値はerror
  2. exec.Command(cmd, args).Output() 実行結果で何かしら出力される場合はこちら。戻り値はoutput結果([]byte型)とerror
package main

import (
  "fmt"
  "os.exec"
)

func main(){
out, err := exec.Command("ls", "-la").Output()
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
}

引数が複数ある時

git add XXXX 等など…

この場合、exec.Command(cmd, args) のargs に git 以下を入れるんですが、argsはslice型で取らないければなりません。 そこで git を例に取るならば add XXXX がargsの中身なり、これはargsは配列でする必要がある。

import (
  // 追加
  "strings"
)

func main (){
  // スペースごとに区切って配列化
  args := Strings.Filelds("add XXXX") // => ["add", "XXXX"]
  out, err := exec.Command("git", args...).Output() 
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
  fmt.Pringln(string(out))

}

exec.Command は第一引数にコマンドを取り、それ以外は第2引数に入れなければならない。

goで実際にコマンドをつらつら書きながら進める場合、上記実装のようにひたすらコマンドの実行と成功の可否(outputとエラー内容)を愚直に確認しながら書いていきます。
もし処理が止まったら必ずexitしましょう。

goからshを呼ぶのはただたんにshellを書いているだけ。むしろそちらよりもgoにコマンドを実行してもらう方法の方がgoでCLIツールを書くには向いているような気がする。