本文详解如何使用 go 的 `os/exec` 包安全、可靠地执行本地 bash 脚本,涵盖路径处理、错误诊断、输出捕获及常见陷阱(如 exit status 127),并提供可直接运行的完整示例。
在 Go 中执行 Bash 脚本看似简单,但实际开发中常因路径错误、权限缺失或 shell 解析机制差异导致失败(例如 exit status 127 —— 这是 Unix/Linux 中“命令未找到”的标准错误码)。你遇到的问题正源于此:exec.Command("/bin/sh", "hello.sh") 尝试在 /bin/sh 目录下查找 hello.sh,而它实际位于当前工作目录(即 hello/ 根目录),因此 shell 无法定位脚本文件。
首先确保 hello.sh 具备可执行权限:
chmod +x hello.sh
然后在 hello.go 中使用 exec.Command 配合 Output() 方法(推荐用于需获取 stdout 的场景):
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
)
func runHelloScript() ([]byte, error) {
// 获取当前 Go 文件所在目录(即 hello/)
exePath, err := os.Executable()
if err != nil {
return nil, fmt.Errorf("failed to get executable path: %w", err)
}
dir := filepath.Dir(exePath) // 或使用 os.Getwd() 获取运行时工作目录
// 构建 hello.sh 的绝对路径
scriptPath := filepath.Join(dir, "hello.sh")
// 执行脚本(注意:/bin/sh 是解释器,scriptPath 是参数)
cmd := exec.Command("/bin/sh", scriptPath)
// 捕获标准输出和标准错误
out, err := cmd.Output()
if err != nil {
// exit status 127 通常意味着脚本路径错误或 /bin/sh 找不到该文件
if exitErr, ok := err.(*exec.ExitError); ok {
log.Printf("Script failed with exit code %d, stderr: %s",
exitErr.ExitCode(), string(exitErr.Stderr))
}
return nil, fmt.Errorf("failed to run hello.sh: %w", err)
}
return out, nil
}
func main() {
output, err := runHelloScript()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Script output: %s", output) // 期望输出类似 ["a", "b", "c"]
}? 提示:若 hello.sh 内容为 echo '["a", "b", "c"]',上述代码将准确打印该 JSON 字符串。
通过以上方法,你不仅能解决 exit status 127 错误,还能构建健壮、可维护的脚本调用逻辑,适用于自动化任务、DevOps 工具或服务启动前的初始化流程。