golang

Go言語でコマンドを実行し、一定時間内に終了しなかったらプロセスを強制終了する

main.go
package main

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

func main() {
    //実行するコマンドを設定
    cmd := exec.Command("コマンド", "オプション")
    //コマンドを実行するときのカレントディレクトリを設定
    cmd.Dir = "コマンドを実行するときのカレントディレクトリ"

    //コマンドを実行開始
    if err := cmd.Start(); err != nil {
        log.Printf("err: %s", err)
    }

    //タイマーを設定
    ticker := *time.NewTicker(XXX * time.Second) //強制終了までの時間を設定

    //並行処理間とステータスを共有するチャンネルを設定
    exit := make(chan bool, 2)

    //並行処理1:タイマーに設定された時間が経過した際に、プロセスを強制終了し、ステータスにTRUEを送信
    go func() {

        //無限ループでタイマーの時間経過をキャッチ
        for {
            select {
                //タイマーに設定された時間が経過した
                case <-ticker.C:

                    //タイマーを終了
                    ticker.Stop()

                    //cmd.ProcessStateは、cmd.Wait()するまでnil
                    if cmd.ProcessState == nil || !cmd.ProcessState.Exited() {

                        //プロセスを強制終了
                        if err := cmd.Process.Kill(); err != nil {
                            log.Printf("err: %s", err)
                        }

                        //ステータスにTRUEを送信
                        exit <-true
                    }
                    return
            }
        }
    }()

    //並行処理2:コマンドが実行終了するまで待ち、終了したらステータスにFALSEを送信
    go func() {

        //処理の終了時にステータスにFALSEを送信することを強制する
        defer func() {exit <-false}()

        //コマンドが実行終了するまで待つ
        if err := cmd.Wait(); err != nil {
            log.Printf("err: %s", err)
        }
    }()

    //チャンネルにステータスが送信するまで待つ
    isKilled := <-exit

    //受信したステータスに応じて処理を分岐
    if isKilled {
        //プロセスが強制終了した際の処理
        log.Print("killed")
    } else if cmd.ProcessState.Success() {
        //コマンドが正常終了した際の処理
        log.Print("success")
    } else {
        //コマンドがエラー終了した際の処理
        log.Print("fail")
    }
}

参考ページ

コマンドの各種リファレンス:https://golang.org/pkg/os/exec/
タイマーのリファレンス:https://golang.org/pkg/time/#Ticker
チャンネルあれこれ:https://qiita.com/najeira/items/47539ab346fa0c00dc62

変更履歴

2017/11/07
 コマンドを実行するカレントディレクトリの指定方法を、os.Chdir()からcmd.Dirの指定へ変更しました。