17. go执行外部命令
目录
1 执行外部命令
1.1 1. 方式一:run
-
code
1 2 3 4 5 6 7
func main() { cmd := exec.Command("ls", "-l", "/var/log/") err := cmd.Run() if err != nil { log.Fatalf("cmd.Run() failed with %s\n", err) } }
Run()
方法会启动命令并等待命令执行完毕。它会阻塞当前 goroutine 直到命令执行完毕,并返回一个error
对象,该对象表示命令执行的错误信息。如果命令执行成功,Run()
方法会返回nil
- 直接调用 Cmd 对象的 Run 函数,返回的只有成功和失败,获取不到任何输出的结果
1.2 2. 方式二:start & wait
-
code
1 2 3 4 5 6 7 8 9 10
func main() { // 使用 Start() 方法启动命令 cmd = exec.Command("ping", "www.baidu.com") if err := cmd.Start(); err != nil { fmt.Println("Error:", err) } if err := cmd.Wait(); err != nil { fmt.Println("Error:", err) } }
-
Start()
方法会启动命令并立即返回。它不会等待命令执行完毕,而是会在后台异步执行命令。Start()
方法返回一个error
对象,该对象表示启动命令的错误信息。如果命令启动成功,Start()
方法会返回nil
-
在使用
Start()
方法启动命令后,我们可以使用Wait()
方法等待命令执行完毕。Wait()
方法会阻塞当前 goroutine 直到命令执行完毕,并返回一个error
对象,该对象表示命令执行的错误信息。如果命令执行成功,Wait()
方法会返回nil
2 输出日志
2.1 1. 标准输出
-
code
1 2 3 4 5 6 7 8 9
func main() { cmd := exec.Command("cal") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { log.Fatalf("cmd.Run() failed: %v\n", err) } }
2.2 2. 转存文件
-
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14
func main() { f, err := os.OpenFile("out.txt", os.O_WRONLY|os.O_CREATE, os.ModePerm) if err != nil { log.Fatalf("os.OpenFile() failed: %v\n", err) } cmd := exec.Command("cal") cmd.Stdout = f cmd.Stderr = f err = cmd.Run() if err != nil { log.Fatalf("cmd.Run() failed: %v\n", err) } }
2.3 3. 发送到网络
-
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
func cal(w http.ResponseWriter, r *http.Request) { year := r.URL.Query().Get("year") month := r.URL.Query().Get("month") cmd := exec.Command("cal", month, year) cmd.Stdout = w cmd.Stderr = w err := cmd.Run() if err != nil { log.Fatalf("cmd.Run() failed: %v\n", err) } } func main() { http.HandleFunc("/cal", cal) http.ListenAndServe(":8080", nil) }
2.4 4. 手动捕获
-
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
package middleware import ( "bufio" "fmt" "git-biz.qianxin-inc.cn/upming/component/sdk-go-framework.git/log" "io" "os/exec" ) type stdType int32 const ( stdTypeStdout stdType = iota + 1 stdTypeStderr ) func forkStdLog(cmd *exec.Cmd) error { // 捕获标准输出 stdout, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("cmd.StdoutPipe() failed with %v", err) } go func() { printExecStd(bufio.NewReader(stdout)) }() // 捕获标准错误 stderr, err := cmd.StderrPipe() if err != nil { return fmt.Errorf("cmd.StderrPipe() failed with %v", err) } go func() { // printExecStd(bufio.NewReader(stderr), stdTypeStderr) // TODO 中间件s的输出不标准,后期再处理,需要加上这个参数 printExecStd(bufio.NewReader(stderr)) }() return nil } func printExecStd(reader *bufio.Reader, std ...stdType) { logger := log.WithField("[ middleware_s ]", "printExecStd") var s stdType if len(std) > 0 { s = std[0] } else { s = stdTypeStdout } outputBytes := make([]byte, 1024) for { n, err := reader.Read(outputBytes) // 获取屏幕的实时输出(并不是按照回车分割) if err != nil { if err == io.EOF { break } logger.Errorf("read %s failed with %v", std, err) } output := string(outputBytes[:n]) if s == stdTypeStdout { logger.Info(output) } else if s == stdTypeStderr { logger.Error(output) } } }
Buy me a coffee~

