
本文介绍在 Go 程序退出后仍持续运行外部命令,并将标准输出(及标准错误)可靠重定向至文件的正确方法——通过 shell(如 sh 或 bash)执行带重定向语法的命令字符串。
本文介绍在 go 程序退出后仍持续运行外部命令,并将标准输出(及标准错误)可靠重定向至文件的正确方法——通过 shell(如 `sh` 或 `bash`)执行带重定向语法的命令字符串。
在 Go 中使用 os/exec 启动子进程时,exec.Command() 默认不支持 Shell 特性(如 >、>>、2>&1 等重定向操作符)。直接将 ">" 作为参数传入(如 exec.Command("script.sh", ">", "out.log"))会被当作脚本的普通字符串参数,而非由 Shell 解析的 I/O 指令,因此无法实现预期重定向。
✅ 正确做法是:委托系统 Shell 执行完整命令行,利用 Shell 自身的重定向能力。例如:
package main
import (
"fmt"
"os/exec"
)
func main() {
// ✅ 正确:用 sh -c 执行带重定向的命令字符串
cmd := exec.Command("sh", "-c", "/tmp/test.sh > /tmp/out 2>&1")
if err := cmd.Start(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to start detached process: %v\n", err)
return
}
fmt.Printf("Detached process started with PID: %d\n", cmd.Process.Pid)
// Go 主程序可立即退出,/tmp/test.sh 仍会持续运行并写入 /tmp/out
}
? 关键说明:
"sh"是调用的 Shell 可执行文件(也可替换为/bin/bash、/usr/bin/zsh等);"-c"表示后续参数是一个待执行的命令字符串;- 第三个参数
"/tmp/test.sh > /tmp/out 2>&1"是单个字符串,由 Shell 解析并执行,其中2>&1将 stderr 合并到 stdout,确保所有输出均写入文件。
? 注意事项:
- 确保
/tmp/test.sh具有可执行权限(chmod +x /tmp/test.sh); - 若脚本路径含空格或特殊字符,需在 shell 字符串中正确转义(推荐避免路径含空格);
- 不要调用
cmd.Wait()或cmd.Run()—— 这会使 Go 程序阻塞等待子进程结束,违背“detached”初衷; - 若需完全脱离父进程(防止 SIGHUP 中断),可进一步结合
setsid(Linux/macOS):cmd := exec.Command("sh", "-c", "setsid /tmp/test.sh > /tmp/out 2>&1")
? 总结:Go 的 exec.Command 本身不解析 Shell 语法,但可通过 sh -c "..." 模式安全、简洁地复用 Shell 的 I/O 重定向与进程管理能力,是实现后台长期运行+日志持久化的标准实践。










