os/exec.Command 默认不连接标准流,需显式调用 CombinedOutput() 等方法获取输出;参数须拆分为独立字符串以避免 shell 注入;错误需区分启动失败与退出码非0;跨平台需用 LookPath 并注意内置命令差异。

os/exec.Command 启动命令时为什么没输出?
因为 Command 默认不连接 stdin/stdout/stderr,只创建进程,不自动捕获或转发流。常见现象是程序“卡住”或返回空结果,尤其在调用 ls、curl 等有输出的命令时。
- 必须显式调用
cmd.CombinedOutput()(推荐用于调试)、cmd.Output()或cmd.Run()+ 自行配置StdoutPipe才能拿到结果 - 如果只调用
cmd.Start()而不Wait(),进程可能被孤儿化,且无法判断是否执行成功 - 注意:
Output()和CombinedOutput()会阻塞直到命令退出;若命令长期运行(如tail -f),应改用Start()+ 流式读取
传参带空格或特殊字符时命令失败怎么办?
os/exec.Command 不经过 shell 解析,参数是原样传递给可执行文件的——这是安全优势,也是易错点。错误写法:Command("sh", "-c", "echo hello world") 看似合理,但实际绕过了 Go 的参数隔离机制,引入 shell 注入风险。
- 正确做法是把每个参数拆成独立字符串:比如执行
find /tmp -name "*.log",应写为Command("find", "/tmp", "-name", "*.log") - 通配符(
*、?)不会被自动展开,"*.log"会字面传给find,它自己处理 glob;若想由 Go 展开,得先用filepath.Glob - 路径含空格无需额外转义,
Command("cp", "/path/with space/file.txt", "/dest/")是合法且安全的
如何判断命令是否真的执行失败?
err 返回值不等于“命令退出码非 0”。它只表示进程启动失败(如找不到二进制、权限不足),而命令内部报错(如 curl 404、git 没有仓库)仍会返回 nil err,需检查 *exec.ExitError。
- 用
err != nil只能确认“根本没跑起来”;要判断业务失败,必须看cmd.CombinedOutput()返回的exitCode - 标准方式:
output, err := cmd.CombinedOutput() if err != nil { if exitErr, ok := err.(*exec.ExitError); ok { fmt.Println("exit code:", exitErr.ExitCode()) } } - 别依赖
os.IsNotExist(err)判断命令是否存在——它只对LookPath有效;Command构造阶段不校验,失败发生在Run()或Start()时
跨平台执行命令要注意哪些兼容性细节?
Windows 和 Unix-like 系统对可执行文件查找、路径分隔符、内置命令的支持差异明显,os/exec 不做抽象,直接暴露底层行为。
立即学习“go语言免费学习笔记(深入)”;
-
ping在 Windows 是ping.exe,Linux 是/bin/ping;不要硬编码路径,用exec.LookPath("ping")动态查找 - Windows 下
dir、copy是 cmd 内置命令,不能直接Command("dir");必须走Command("cmd", "/C", "dir") -
os/exec默认不继承父进程环境变量,如需PATH,要手动设置:cmd.Env = os.Environ();否则Command("git")在某些环境下会报 “executable file not found”










