为何Golang中exec.Cmd.Wait执行rollup -w监听命令时会立即返回?
问题:Go执行rollup -w时exec.Cmd.Wait()立即返回
我用Go的exec.Command执行rollup -w命令(该命令用于监听文件变更,正常会持续运行),但发现exec.Cmd.Wait()在执行后立即触发,按预期Wait()应该等待命令执行完成才对,为什么会出现这种情况?
代码片段
执行命令的工具函数:
func runCommand(cmd *exec.Cmd) error { outpipe, err := cmd.StdoutPipe() if err != nil { return err } errpipe, err := cmd.StderrPipe() if err != nil { return err } if err = cmd.Start(); err != nil { return err } var wg sync.WaitGroup readPipe := func(pipe io.ReadCloser) { defer wg.Done() scanner := bufio.NewScanner(pipe) for scanner.Scan() { line := scanner.Text() printProc(line) } } wg.Add(1) go readPipe(outpipe) wg.Add(1) go readPipe(errpipe) // 预期这里不会立即执行到 if err = cmd.Wait(); err != nil { return err } wg.Wait() return nil }
调用执行rollup的函数:
func roll() error { cmdRoll := exec.Command("npm", "run", "roll") printProc("running roll command") if err := runCommand(cmdRoll); err != nil { return err } printProc("roll command executed successfully") return nil }
package.json中的对应脚本:
"roll": "rimraf build && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript -w"
原因分析
核心问题出在你通过npm run启动rollup,而非直接调用rollup进程:
npm run的默认行为是启动目标脚本里的命令后,不会持续绑定自身生命周期到子进程。当npm完成脚本的启动流程(比如执行完rimraf build并启动rollup后),它会直接退出,导致Go中管控的npm进程终止,cmd.Wait()检测到进程结束就立即返回了——但此时rollup的监听进程可能还在后台运行,只是已经脱离了Go进程的管控范围。
解决方案
方案1:直接调用rollup进程(推荐)
跳过npm run,直接用exec.Command调用rollup命令,同时手动执行前置的rimraf build:
func roll() error { // 先执行rimraf清理build目录 rimrafCmd := exec.Command("rimraf", "build") if err := rimrafCmd.Run(); err != nil { return err } // 直接启动rollup监听进程 cmdRoll := exec.Command("rollup", "--config", "rollup.config.ts", "--configPlugin", "@rollup/plugin-typescript", "-w") printProc("running roll command") if err := runCommand(cmdRoll); err != nil { return err } printProc("roll command executed successfully") return nil }
这种方式下,Go进程直接管控rollup,Wait()会一直等待rollup主动退出(比如手动终止程序),符合你的预期。
方案2:修改npm的行为使其等待子进程
可以通过--foreground-scripts参数让npm持续监听子进程,或者在脚本中用exec命令替换原有启动逻辑:
方式A:调用npm时加参数
cmdRoll := exec.Command("npm", "run", "roll", "--", "--foreground-scripts")
方式B:修改package.json脚本
"roll": "rimraf build && exec rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript -w"
不过这种方式依赖npm的版本和系统环境,可靠性不如直接调用rollup。
内容的提问来源于stack exchange,提问作者Sayyid J
相关产品推荐
相关产品推荐

