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 输出日志

https://darjun.github.io/2022/11/01/godailylib/osexec/

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~
Fred 支付宝支付宝
Fred 微信微信
0%