Paket ini adalah pembungkus kecil namun sangat berguna di sekitar os/exec.Cmd yang membuatnya aman dan sederhana untuk menjalankan perintah eksternal dalam aplikasi real-time yang sangat bersamaan, asinkron. Ini berfungsi di Linux, macOS, dan Windows. Inilah penggunaan dasarnya:
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
}
Itu saja, hanya tiga metode: Start
, Stop
, dan Status
. Jika memungkinkan, lebih baik menggunakan go-cmd/Cmd
daripada os/exec.Cmd
karena go-cmd/Cmd
menyediakan:
Seperti yang diperlihatkan contoh di atas, memulai suatu perintah akan segera mengembalikan saluran ke mana status akhir dikirim ketika perintah keluar karena alasan apa pun. Jadi secara default, perintah dijalankan secara asinkron, tetapi menjalankan secara sinkron juga dimungkinkan dan mudah:
// Run foo and block waiting for it to exit
c := cmd . NewCmd ( "foo" )
s := <- c . Start ()
Untuk mencapai hal serupa dengan os/exec.Cmd
memerlukan semua yang sudah dilakukan paket ini.
Biasanya ingin membaca stdout atau stderr saat perintah sedang berjalan. Pendekatan umum adalah memanggil StdoutPipe dan membaca dari io.ReadCloser
yang disediakan. Ini berfungsi tetapi salah karena menyebabkan kondisi balapan (yang terdeteksi go test -race
) dan dokumen mengatakan itu salah:
Oleh karena itu, memanggil Tunggu sebelum semua pembacaan dari pipa selesai adalah salah. Untuk alasan yang sama, memanggil Run saat menggunakan StdoutPipe adalah salah.
Solusi yang tepat adalah dengan mengatur io.Writer
dari Stdout
. Agar thread-safe dan tidak terburu-buru, hal ini memerlukan upaya lebih lanjut untuk menulis sementara mungkin ada N-banyak goroutine yang dibaca. go-cmd/Cmd
telah melakukan pekerjaan ini.
Mirip dengan stdout dan stderr real-time, menyenangkan untuk melihat, misalnya, runtime yang telah berlalu. Paket ini memungkinkan: Status
dapat dipanggil kapan saja oleh goroutine mana pun, dan mengembalikan struct ini:
type Status struct {
Cmd string
PID int
Complete bool
Exit int
Error error
Runtime float64 // seconds
Stdout [] string
Stderr [] string
}
Berbicara tentang struct di atas, Cmd
bawaan Go tidak meletakkan semua informasi pengembalian di satu tempat, dan itu tidak masalah karena Go itu luar biasa! Namun untuk menghemat waktu, go-cmd/Cmd
menggunakan struct Status
di atas untuk menyampaikan semua informasi tentang perintah tersebut. Bahkan ketika perintah selesai, pemanggilan Status
mengembalikan status akhir, status akhir yang sama dikirim ke saluran status yang dikembalikan oleh panggilan ke Start
.
os/exec/Cmd.Wait dapat memblokir bahkan setelah perintah dimatikan. Hal ini dapat mengejutkan dan menimbulkan masalah. Tapi go-cmd/Cmd.Stop
dengan andal menghentikan perintah, tidak ada kejutan. Masalah ini berkaitan dengan ID grup proses. Merupakan hal yang umum untuk mematikan perintah PID, tetapi biasanya kita perlu mematikan ID grup prosesnya. go-cmd/Cmd.Stop
mengimplementasikan keajaiban tingkat rendah yang diperlukan untuk mewujudkan hal ini.
Selain cakupan pengujian 100% dan tidak ada kondisi balapan, paket ini secara aktif digunakan di lingkungan produksi.
Brian Ip menulis kode asli untuk mendapatkan status keluar. Anehnya, Go tidak hanya menyediakan ini, ia memerlukan keajaiban seperti exiterr.Sys().(syscall.WaitStatus)
dan banyak lagi.
MIT © pergi-Cmd.