You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:03:28