捕获child_process spawnSync或execSync的标准输出

3
有没有简单的方法可以捕获child_process spawnSync或execSync的stdout/stderr?我看到其他帖子在几年前说那时不可能,因为它是同步的。
我有一个问题,我需要捕捉另一个线程的输出,以便解析发送到stdout的内容。问题在于,除了使用spawn并使用stdout.on事件外,我没有找到任何其他解决方案。
这会导致问题,因为我的程序不是异步的。
编辑:
我使用的工具称为eslint-watch。它添加了像文件监视和指定默认目录之类的eslint功能。问题在于,在这个https://github.com/eslint/eslint/issues/2831讨论中,我们得出结论,我需要将eslint作为二进制文件执行,并从中获取帮助选项。
在 eslint 返回其帮助选项后,我会解析它们并将它们传递给 opinionator。因此,我的包装器具有与 eslint 相同的帮助上下文菜单,但也包含我的命令。问题是获取帮助是我做的第一件事情之一,因此我可以从终端解析命令。

对于像我这样只想进行简单的标准输出捕获的人来说,有一个提示:从Node v4.3开始,你可以使用capturedOutput = child_process.execSync("your_subprocess")。 更多细节请参阅当前Node版本的文档:https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options - TanguyP
3个回答

6

您编辑以后情况有所改变。

execSync现在会在完成后返回stdout。

//will return date() on *nix systems;
console.log(require("child_process").execSync(`date`).toString());

>>> Fri Jul 10 14:19:41 UTC 2020

3

涉及require('child_process').spawnSync()或.exexSync(),Node API文档明确指出:

这些方法是同步的,意味着它们会阻塞事件循环,暂停代码的执行,直到生成的进程退出。

像这样的阻塞调用在简化通用脚本任务和简化启动时应用程序配置的加载/处理方面非常有用。

所以,你是正确的。在同步运行外部进程时不可能处理stdio事件,因为Node事件循环在外部进程完成之前被停止。

您可以考虑使用shell输出重定向(即使用bash:COMMAND 1>stdout.txt 2>stderr.log)将进程的stdout和/或stderr捕获到一个或多个文件中,您可以通过在bash shell中运行命令来完成此操作。

从Node最简单的方法是:

file:runner.sh

#/bin/sh
PATH_TO_YOUR_COMMAND_HERE 1>command_stdout.txt 2>command_stderr.txt

file:nodeprocess.js

var fs = require('fs'),
    spawnSync = require('child_process').spawnSync,
    args = ['/bin/sh', 'runner.sh'];

spawnSync(args); // Event Loop will wait here.

if(fs.existsSync('./command_stdout.txt')){
  // process standard output from your command here
}
if(fs.existsSync('./command_stderr.txt')){
  // process error output from your command here
}

当然,你应该做所有适当的文件系统清理工作,如将输出文件写入/tmp并在完成后删除它们,但这些事情就交给你的想象力了。
我很好奇为什么你不能使用.spawn().fork(),因为如果一个命令可以从命令行运行,那么没有理由它不能从Node中异步运行。

大部分我问这个问题是为了看看其他人有什么想法。但似乎询问这个问题是一件负面的事情。:S 如此已经在这里陈述,我不是唯一一个有这种问题的人 https://github.com/joyent/node/issues/9265理想情况下,我希望使用spawnSync(),只因为我创建的工具是另一个命令行工具的包装器。我最近刚将我的应用程序转换为使用spawn(),但结果导致了一个我想要避免的回调/承诺链。 - Rizowski
如果你能描述清楚你想要的内容,我可能能够提供进一步的帮助。 - Rob Raisch
我已经在我的问题中添加了额外的信息。可能没有办法绕过这个问题,我可能需要做一个回调,但我不确定是否还有其他方法可以解决它。 - Rizowski

3

我不确定自己是否理解正确,猜测回答对发布者已经没有兴趣了。不过,由于我曾经遇到类似的问题,这里提供一些示例:

var karma = spawnSync("node",
    ["node_modules/karma/bin/karma", "start", "karma.conf.js", "--single-run"],
    {
        stdio: [null, process.stdout, process.stderr]
    }
);
if (karma.status != 0) {
    console.log(`Karma reported error(s) (${karma.status}). Build aborted\n`.red);
    process.exit(1);
} else {
    console.log(`Karma success\n`.green);
}

上面的示例启动了Karma并执行了配置文件中指定的测试。我想要的是在设置webpack进行发布构建时强制执行测试。这样做的想法是,即使构建有误配置,构建服务器也不能在未经测试的情况下进行发布。
为此,将上述代码块引入webpack.conf.js中,并阻塞执行线程直到karma完成,同时将karma的stdout/err输出到控制台。使用spawnSync而不是spawn只是因为一旦线程从webpack.conf.json返回,Webpack就会立即启动并可能构建失败的项目。
原始问题描述听起来对我来说像是一个异步的事情。调用进程,一旦stdout到达,在事件处理程序中添加您的附加输出。
如果您仍然想要同步执行,stdio数组只需要获取流。这意味着您可以为stdout创建一个包装器流,并告诉子进程使用该流。

我很感激你的建议。我发帖已经一年了。在我的项目中,我不得不做类似的事情。如果我没记错的话,当我尝试使用spawnSync执行Eslint时,无法捕获输出,因为它是直接打印到控制台的。最后,我被迫使用spawn并在结果逐步返回时进行处理。这可能是因为我对stdout和stderr以及它们在嵌套进程中的工作方式缺乏了解。 - Rizowski
不客气。我是从C/Java转到node-js的新手,所以我不能确定,但是从阅读其他示例来看,我认为能够为spawnSync设置stdio是新功能,在你发布问题时还不存在。 - user3240383

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接