如何在Go中让npm install的进度条输出到终端?
如何捕获npm install的进度条输出(Go语言exec包)
问题描述
我尝试用Go的exec.Command结合StdoutPipe/StderrPipe捕获npm install的输出,代码如下:
shell := exec.Command("npm", args...) shell.Dir = cwd outpipe, _ := shell.StdoutPipe() errpipe, _ := shell.StderrPipe() shell.Start() go func(pipe io.ReadCloser) { reader := bufio.NewReader(pipe) line, err := reader.ReadString('\n') for err == nil { fmt.Println(string(line)) line, err = reader.ReadString('\n') } fmt.Println("exited") }(outpipe) go func(pipe io.ReadCloser) { reader := bufio.NewReader(pipe) line, err := reader.ReadString('\n') for err == nil { fmt.Println(string(line)) line, err = reader.ReadString('\n') } fmt.Println("exited") }(errpipe) err := shell.Wait() if err != nil { fmt.Println(err) }
但只能得到最终结果输出:
added 87 packages, and audited 88 packages in 3s 9 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
直接在控制台运行npm install时能看到的进度条完全没捕获到。我还试过直接将输出缓存到bytes.Buffer,结果一样:
shell := exec.Command(command, args...) shell.Dir = cwd var stderr, stdout bytes.Buffer shell.Stderr = &stderr shell.Stdout = &stdout return shell, &stdout, &stderr
请问怎么才能获取到npm install的进度条输出?
原因分析
npm默认会检测当前运行环境是否为交互式TTY终端,如果是非TTY环境(比如通过Go exec调用的子进程),会自动关闭进度条这类动态输出,只返回最终结果。
解决方法
方法1:直接重定向到当前进程的stdout/stderr
最简单的方式是让子进程的输出直接继承当前进程的终端,这样npm会认为是TTY环境,自动输出进度条:
package main import ( "fmt" "os" "os/exec" ) func main() { cwd, _ := os.Getwd() args := []string{"install"} shell := exec.Command("npm", args...) shell.Dir = cwd // 将子进程的stdout和stderr直接重定向到当前进程的输出 shell.Stdout = os.Stdout shell.Stderr = os.Stderr err := shell.Run() if err != nil { fmt.Printf("命令执行失败:%v\n", err) } }
这种方式的效果和直接在控制台运行npm install完全一致,进度条会实时显示。
方法2:使用伪终端(PTY)捕获输出
如果需要同时捕获进度条输出并自定义处理(比如保存到日志),可以使用伪终端库模拟TTY环境,推荐使用github.com/creack/pty:
- 先安装依赖:
go get github.com/creack/pty
- 示例代码:
package main import ( "bufio" "fmt" "os" "os/exec" "github.com/creack/pty" ) func main() { cwd, _ := os.Getwd() args := []string{"install"} shell := exec.Command("npm", args...) shell.Dir = cwd // 创建伪终端,让npm认为处于交互式环境 ptmx, err := pty.Start(shell) if err != nil { panic(err) } defer ptmx.Close() // 读取伪终端的输出(包含进度条) go func() { scanner := bufio.NewScanner(ptmx) for scanner.Scan() { line := scanner.Text() // 这里可以同时处理输出,比如打印到控制台+写入日志 fmt.Println(line) // log.Printf("npm输出:%s", line) } }() // 等待命令执行完成 err = shell.Wait() if err != nil { fmt.Printf("命令执行失败:%v\n", err) } }
方法3:强制npm输出进度条
部分npm版本支持通过命令行参数或环境变量强制开启进度条,不过这种方法兼容性不如前两种:
- 通过命令行参数:
shell := exec.Command("npm", "install", "--progress=true")
- 通过环境变量:
shell := exec.Command("npm", "install") // 添加环境变量强制开启进度条 shell.Env = append(os.Environ(), "NPM_CONFIG_PROGRESS=true")
注意:部分新版本npm可能会忽略该参数,仍以TTY检测结果为准,优先推荐前两种方法。
内容的提问来源于stack exchange,提问作者smolchild
相关产品推荐
相关产品推荐

