执行:显示标准输出“实时”


187

我有这个简单的脚本:

var exec = require('child_process').exec;

exec('coffee -cw my_file.coffee', function(error, stdout, stderr) {
    console.log(stdout);
});

在这里,我只需执行一个命令即可编译咖啡脚本文件。但是stdout永远不会显示在控制台中,因为该命令永远不会结束(因为coffee的-w选项)。如果我直接从控制台执行命令,则会收到如下消息:

18:05:59 - compiled my_file.coffee

我的问题是:是否可以使用node.js exec显示这些消息?如果是,怎么办?!

谢谢


1
我来这里是为了从Python可执行文件中捕获标准输出。请注意,以下所有内容均可使用,但您需要使用“ -u”选项运行python,以使输出不受缓冲,从而进行实时更新。
安迪

Answers:


264

不要使用exec。使用spawn哪个是EventEmmiter对象。然后,您可以在事件发生时收听stdout/ stderrevent(spawn.stdout.on('data',callback..))。

从NodeJS文档中:

var spawn = require('child_process').spawn,
    ls    = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('child process exited with code ' + code.toString());
});

exec 缓冲输出,通常在命令完成执行后将其返回。


22
非常好。仅供参考:stdout / stderr事件回调参数“数据”是一个缓冲区,请使用.toString()调用它
SergeL 2014年

4
对于那些无法在Windows上生成的人来说,看看这个好答案
tomekwi 2014年

17
exec至少也是EventEmitter。
Nikolay Tsenkov

4
还请记住,只要程序输出换行符,就不会调用该回调函数。如果要从子进程接收“事件”,则此进程必须刷新缓冲区(flush(stdout);以C表示)才能在Node.js中触发事件。
朱利安·F·韦纳特

5
exec上的+1也是一个EventEmitter ..花了2个小时将我的字符串重构为args数组(非常长且复杂的ffmpeg命令行)..才发现我并不需要。
deadconversations

176

exec 还将返回作为EventEmitter的ChildProcess对象。

var exec = require('child_process').exec;
var coffeeProcess = exec('coffee -cw my_file.coffee');

coffeeProcess.stdout.on('data', function(data) {
    console.log(data); 
});

pipe子进程的标准输出到主标准输出。

coffeeProcess.stdout.pipe(process.stdout);

或使用spawn继承stdio

spawn('coffee -cw my_file.coffee', { stdio: 'inherit' });

35
看起来可以通过以下方式简化pipecoffeeProcess.stdout.pipe(process.stdout);
Eric Freese

3
@EricFreese的评论正是我想要的,因为我想利用stdout的字符替换功能(在节点脚本中利用量角器)
LoremIpsum

19
更简单:spawn(cmd, argv, { stdio: 'inherit' })。有关不同的示例,请参见nodejs.org/api/child_process.html#child_process_options_stdio
Morgan Touverey Quilling

3
为@MorganTouvereyQuilling的spawn与一起使用的建议+1 stdio: 'inherit'。例如,当显示来自的进度信息时,它比execstdout/ 产生更准确的输出。stderrgit clone
利夫温

55

目前已经有好几个答案但他们没有提及的最佳(和最简单的)的方式来做到这一点,这是使用spawn{ stdio: 'inherit' }选项。例如,当显示来自的进度信息时,它似乎会产生最准确的输出git clone

只需执行以下操作:

var spawn = require('child_process').spawn;

spawn('coffee', ['-cw', 'my_file.coffee'], { stdio: 'inherit' });

感谢@MorganTouvereyQuilling在此评论中指出了这一点


1
我发现当子流程使用格式化输出(如彩色文本)时,会stdio: "inherit"保留该格式化,而child.stdout.pipe(process.stdout)不会保留。
Rikki Gibson

即使在具有复杂输出的进程(例如npm安装上的进度条)上,这也可以完美地保留输出。太棒了!
Dave Koo

1
为什么这不是公认的答案?这是唯一为我工作的,只有2条f *线!!!
林肯

当执行一些使用进度条的Symfony命令行应用程序时,此技巧很有帮助。干杯。
半停

这应该是公认的答案,只有这样才能保留完美的输出表示这是最简单的吗?是的,请
evnp

21

我只想添加一个小问题,即从生成的进程中输出缓冲字符串,console.log()因为它添加了换行符,可以将生成的进程输出扩展到其他行中。如果输出stdoutstderrprocess.stdout.write()代替console.log(),那么你会得到从衍生进程“是”输出控制台。

我在这里看到了该解决方案: Node.js:无需尾随换行符即可打印到控制台吗?

希望对使用上述解决方案的人有所帮助(即使来自文档,这也是实时输出的绝佳解决方案)。


1
为了获得更准确的输出使用spawn(command, args, { stdio: 'inherit' }),如@MorganTouvereyQuilling在此处建议的那样stackoverflow.com/questions/10232192/…–
Livven

20

受到Nathanael Smith的回答和Eric Freese的评论的启发,它可能很简单:

var exec = require('child_process').exec;
exec('coffee -cw my_file.coffee').stdout.pipe(process.stdout);

这对于简单的命令(如)似乎可以正常工作,ls但对于诸如的更复杂的命令却无法解决npm install。我什至尝试将stdout和stderr管道输送到它们各自的过程对象。
linuxdan

@linuxdan可能是因为npm在stderr中编写(我看到有人在其中写进度条)。您还可以通过管道传输stderr,或扩展Tongfa解决方案以收听stderr。
塞尔吉(Sergiu)

@linuxdan从我所看到的最可靠的方法是spawn(command, args, { stdio: 'inherit' }),如此处建议stackoverflow.com/questions/10232192/…–
Livven

12

我发现将自定义exec脚本添加到执行此操作的实用程序中很有帮助。

utilities.js

const { exec } = require('child_process')

module.exports.exec = (command) => {
  const process = exec(command)

  process.stdout.on('data', (data) => {
    console.log('stdout: ' + data.toString())
  })

  process.stderr.on('data', (data) => {
    console.log('stderr: ' + data.toString())
  })

  process.on('exit', (code) => {
    console.log('child process exited with code ' + code.toString())
  })
}

app.js

const { exec } = require('./utilities.js')

exec('coffee -cw my_file.coffee')

5

在回顾了所有其他答案之后,我得出了以下结论:

function oldSchoolMakeBuild(cb) {
    var makeProcess = exec('make -C ./oldSchoolMakeBuild',
         function (error, stdout, stderr) {
             stderr && console.error(stderr);
             cb(error);
        });
    makeProcess.stdout.on('data', function(data) {
        process.stdout.write('oldSchoolMakeBuild: '+ data);
    });
}

有时data会是多行,因此oldSchoolMakeBuild标题会在多行中出现一次。但这并没有让我烦恼去更改它。


3

child_process.spawn返回带有stdout和stderr流的对象。您可以点击stdout流以读取子进程发送回Node的数据。作为流的标准输出具有“数据”,“结束”和流具有的其他事件。当您希望子进程将大量数据返回到Node时,最好使用spawn-图像处理,读取二进制数据等。

因此您可以使用child_process.spawn来解决您的问题,如下所示。

var spawn = require('child_process').spawn,
ls = spawn('coffee -cw my_file.coffee');

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('code ' + code.toString());
});

1

这是一个用打字稿编写的异步帮助器函数,似乎对我有用。我猜想这对于长期存在的进程将不起作用,但对于某些人来说还是很方便的?

import * as child_process from "child_process";

private async spawn(command: string, args: string[]): Promise<{code: number | null, result: string}> {
    return new Promise((resolve, reject) => {
        const spawn = child_process.spawn(command, args)
        let result: string
        spawn.stdout.on('data', (data: any) => {
            if (result) {
                reject(Error('Helper function does not work for long lived proccess'))
            }
            result = data.toString()
        })
        spawn.stderr.on('data', (error: any) => {
            reject(Error(error.toString()))
        })
        spawn.on('exit', code => {
            resolve({code, result})
        })
    })
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.