このパッケージは、小さいながらも非常に便利な os/exec.Cmd のラッパーで、同時性の高い非同期リアルタイム アプリケーションで外部コマンドを安全かつ簡単に実行できるようにします。 Linux、macOS、Windows で動作します。基本的な使い方は次のとおりです。
import (
"fmt"
"time"
"github.com/go-cmd/cmd"
)
func main () {
// Start a long-running process, capture stdout and stderr
findCmd := cmd . NewCmd ( "find" , "/" , "--name" , "needle" )
statusChan := findCmd . Start () // non-blocking
ticker := time . NewTicker ( 2 * time . Second )
// Print last line of stdout every 2s
go func () {
for range ticker . C {
status := findCmd . Status ()
n := len ( status . Stdout )
fmt . Println ( status . Stdout [ n - 1 ])
}
}()
// Stop command after 1 hour
go func () {
<- time . After ( 1 * time . Hour )
findCmd . Stop ()
}()
// Check if command is done
select {
case finalStatus := <- statusChan :
// done
default :
// no, still running
}
// Block waiting for command to exit, be stopped, or be killed
finalStatus := <- statusChan
}
メソッドはStart
、 Stop
、 Status
3 つだけです。 go-cmd/Cmd
には次の機能があるため、可能な場合はos/exec.Cmd
よりもgo-cmd/Cmd
を使用することをお勧めします。
上の例が示すように、コマンドを開始すると、何らかの理由でコマンドが終了したときに最終ステータスが送信されるチャネルがすぐに返されます。したがって、デフォルトではコマンドは非同期的に実行されますが、同期的に実行することも可能で簡単です。
// Run foo and block waiting for it to exit
c := cmd . NewCmd ( "foo" )
s := <- c . Start ()
os/exec.Cmd
で同様のことを実現するには、このパッケージがすでに実行しているすべてのことが必要です。
コマンドの実行中にstdout または stderr を読み取りたい場合はよくあります。一般的なアプローチは、 StdoutPipe を呼び出し、提供されたio.ReadCloser
から読み取ることです。これは機能しますが、競合状態( go test -race
detects)を引き起こすため間違っており、ドキュメントにはそれが間違っていると記載されています。
したがって、パイプからのすべての読み取りが完了する前に Wait を呼び出すのは正しくありません。同じ理由で、StdoutPipe を使用するときに Run を呼び出すことは正しくありません。
適切な解決策は、 Stdout
のio.Writer
設定することです。スレッドセーフでレース性のないものにするためには、おそらく N 個のゴルーチンが読み取られる間に、さらなる書き込み作業が必要になります。 go-cmd/Cmd
この作業を行いました。
リアルタイムの stdout および stderr と同様に、経過したランタイムなどを確認できるのは便利です。このパッケージにより、次のことが可能になります。 Status
任意の goroutine からいつでも呼び出すことができ、次の構造体を返します。
type Status struct {
Cmd string
PID int
Complete bool
Exit int
Error error
Runtime float64 // seconds
Stdout [] string
Stderr [] string
}
上記の構造体について言えば、Go の組み込みCmd
すべての戻り情報を 1 か所にまとめませんが、Go は素晴らしいので問題ありません。ただし、時間を節約するために、 go-cmd/Cmd
上記のStatus
構造体を使用してコマンドに関するすべての情報を伝えます。コマンドが終了した場合でも、 Status
呼び出すと最終ステータスが返されます。同じ最終ステータスが、 Start
の呼び出しによって返されるステータス チャネルに送信されます。
os/exec/Cmd.Wait は、コマンドが強制終了された後でもブロックされる可能性があります。それは驚くべきことであり、問題を引き起こす可能性があります。ただし、 go-cmd/Cmd.Stop
驚くことではありませんが、コマンドを確実に終了します。この問題はプロセス グループ ID に関係しています。コマンド PID を強制終了するのが一般的ですが、通常は代わりにそのプロセス グループ ID を強制終了する必要があります。 go-cmd/Cmd.Stop
これを実現するために必要な低レベルのマジックを実装します。
100% のテスト カバレッジと競合状態がないことに加えて、このパッケージは実稼働環境で積極的に使用されています。
Brian Ip は、終了ステータスを取得するための元のコードを作成しました。奇妙なことに、Go はこれを提供するだけでなく、 exiterr.Sys().(syscall.WaitStatus)
などの魔法を必要とします。
MIT © go-Cmd.