[TOC] # 架构 ![](https://box.kancloud.cn/9a0f7963a8db70369dcf8109b8ae72d4_2974x1482.png) ## shell调用 ~~~ import ( "fmt" "os/exec" ) func main() { var ( cmd *exec.Cmd output []byte err error ) //生成cmd cmd = exec.Command("/usr/local/bin/bash", "-c", "sleep 5; ls -lah; echo 'hello'") //执行了命令,捕获了子进程的输出(pipe) if output, err = cmd.CombinedOutput(); err != nil { fmt.Println(err) return } //打印子进程输出,要转换为string不然是字节数组 fmt.Println(string(output)) } ~~~ ## 可终止调用 ~~~ import ( "context" "fmt" "os/exec" "time" ) //定义结构体,把进程的错误和输出赋值给他 type result struct { err error output []byte } func main() { var ( ctx context.Context cancelFunc context.CancelFunc cmd *exec.Cmd //一个通信协程 resultChan chan *result res *result ) //创建了一个通信协程 resultChan = make(chan *result, 1000) //外面上下文继承了里面参数的上下文 // 他会创建一个上下文和取消上下文函数 /** context: 里面有个chan byte cancelFunc: close(chan byte)他做的事情是把chan关闭了 */ ctx, cancelFunc = context.WithCancel(context.TODO()) //执行一个cmd,让他在一个协程中执行 go func() { var ( output []byte err error ) //命令和上下文交互 //他内部维护了context这个对象,他内部会select{case <- ctx.Done()}监听这个channel是否关闭,如果发现关闭就会kill pid杀死子进程 cmd = exec.CommandContext(ctx, "/usr/local/bin/bash", "-c", "sleep 2; ls -lah; echo 'hello'") //执行任务捕获输出 output, err = cmd.CombinedOutput() //结果输出出去 resultChan <- &result{ err: err, output: output, } }() //几秒后取消上下文,可以改下让他子协程不能输出 time.Sleep(3 * time.Second) //取消上下文 cancelFunc() //在main协程里面,等待子协程退出,并打印任务执行结果 res = <-resultChan //打印任务执行结果 fmt.Println(res.err, string(res.output)) } ~~~