이 패키지는 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
의 세 가지 메서드만 있습니다. 가능하다면 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
감지)을 일으키고 문서에서는 이것이 잘못되었다고 말하기 때문에 잘못된 것입니다.
따라서 파이프의 모든 읽기가 완료되기 전에 Wait를 호출하는 것은 올바르지 않습니다. 같은 이유로 StdoutPipe를 사용할 때 Run을 호출하는 것은 올바르지 않습니다.
적절한 해결책은 Stdout
의 io.Writer
를 설정하는 것입니다. 스레드로부터 안전하고 비레이시(non-racey)를 위해서는 N개의 고루틴을 읽는 동안 쓰기 위한 추가 작업이 필요합니다. go-cmd/Cmd
이 작업을 수행했습니다.
실시간 stdout 및 stderr과 유사하게, 예를 들어 경과된 런타임을 확인하는 것이 좋습니다. 이 패키지는 다음을 허용합니다: Status
모든 고루틴에서 언제든지 호출할 수 있으며 다음 구조체를 반환합니다.
type Status struct {
Cmd string
PID int
Complete bool
Exit int
Error error
Runtime float64 // seconds
Stdout [] string
Stderr [] string
}
위의 구조체에 대해 말하면 Go 내장 Cmd
모든 반환 정보를 한 곳에 저장하지 않습니다. 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.