如何使用Golang os/exec执行外部命令_Golang os/exec命令执行示例

Go中执行外部命令需正确使用exec.Command:Run()适合无需输出的场景;Output()一行获取stdout;Stdin/Stdout/Stderr管道实现细粒度控制;须防shell注入、设超时、控环境变量与工作目录。

使用 os/exec 包执行外部命令是 Go 中很常见的需求,核心在于正确创建、配置并运行 *exec.Cmd 实例。关键不是“能不能跑”,而是“怎么控制输入输出、捕获错误、避免阻塞、防止 shell 注入”。

基础执行:Run() 最简调用

Run() 适合不需要读取输出、只关心命令是否成功的情况(比如 git commitcp)。

  • 它会自动等待命令结束,返回 error(非零退出码也会转为 error)
  • 标准输入/输出/错误都继承自当前进程(即打印到终端)
  • 不推荐用于需要获取输出的场景

示例:

```go
cmd := exec.Command("ls", "-l", "/tmp")
err := cmd.Run()
if err != nil {
  log.Fatal(err)
}
```

获取输出:Output() 一行拿回 stdout

Output() 自动重定向 stdout 到内存,返回 []byte 和 error,适合简单命令如 datehostnamecat file

  • stderr 不被捕获,仍输出到终端(除非显式重定向)
  • 命令超时或崩溃会返回 error,stdout 内容可能为空
  • 注意大输出可能吃内存,慎用于 find / | head -1000 类命令

示例:

```go
out, err := exec.Command("date").Output()
if err != nil {
  log.Fatal(err)
}
fmt.Println(string(out)) // 输出类似 "Wed Jun 12 10:24:33 CST 2025\n"
```

细粒度控制:Stdin/Stdout/Stderr 手动接管

当需要实时处理流、双向通信、或分别捕获 stdout/stderr 时,用 StdinPipeStdoutPipeStderrPipe

  • 必须在 Start() 前调用 Pipe 方法,否则 panic
  • Start() 启动命令但不等待,之后可读写管道、再调用 Wait()
  • 常见组合:用 bytes.Bufferio.MultiWriter 收集输出

示例(捕获 stdout + stderr 分开):

```go
cmd := exec.Command("sh", "-c", `echo "hello"; echo "world" >&2`)

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
  log.Fatal(err)
}

fmt.Println("stdout:", stdout.String()) // "hello\n"
fmt.Println("stderr:", stderr.String()) // "world\n"
```

安全与实用细节

绕不开的几个实际问题:

  • 别拼接字符串传给 Command:避免 shell 注入。用 exec.Command("grep", "-r", userInput, "."),而不是 exec.Command("sh", "-c", "grep -r '"+userInput+"' .")
  • 设置超时:用 context.WithTimeout 传给 CommandContext,比手动 goroutine + timer 更可靠
  • 环境变量控制:修改 cmd.Env 可覆盖或添加环境变量(默认继承 os.Environ)
  • 工作目录:设置 cmd.Dir 指定执行路径

带超时的完整示例:

```go
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, "sleep", "10")
err := cmd.Run()
if err != nil {
  if ctx.Err() == context.DeadlineExceeded {
    fmt.Println("command timed out")
  } else {
    log.Fatal(err)
  }
}
```

基本上就这些。用对 Run/Output/Start+Wait 三种模式,再注意安全和超时,90% 的外部命令需求都能稳稳拿下。