Javascript 如何从child_进程中取消缓冲或至少按顺序获取stdout和stderr
下面是一个简单的节点程序:(注意:这只是一个示例。替换任何同时输出到stdout和stderr的程序,如果这些行的顺序不正确,它们就没有那么有用了,例如llvm、gcc、clang、less、grep等等。) 如果我运行它,我会得到以下输出:Javascript 如何从child_进程中取消缓冲或至少按顺序获取stdout和stderr,javascript,node.js,Javascript,Node.js,下面是一个简单的节点程序:(注意:这只是一个示例。替换任何同时输出到stdout和stderr的程序,如果这些行的顺序不正确,它们就没有那么有用了,例如llvm、gcc、clang、less、grep等等。) 如果我运行它,我会得到以下输出: log: line1 error: line2 log: line3 error: line4 现在我希望从child\u process.execFile获得相同的输出。问题是缓存了这些行,因此它们不会以相同的顺序到达我这里: const child_
log: line1
error: line2
log: line3
error: line4
现在我希望从child\u process.execFile
获得相同的输出。问题是缓存了这些行,因此它们不会以相同的顺序到达我这里:
const child_process = require('child_process');
const lines = [];
const ch = child_process.execFile(process.argv[0], ['main.js'], () => {
console.log(lines.join(''));
});
ch.stdout.on('data', (data) => { lines.push(data); });
ch.stderr.on('data', (data) => { lines.push(data); });
产生:
log: line1
error: line2
error: line4
log: line3
另一个例子是bash脚本:
#!/bin/sh
echo line 1
echo line 2 >&2
echo line 3
echo line 4 >&2
在终端中运行,输出为:
line 1
line 2
line 3
line 4
但是通过子进程运行它
:
const child_process = require('child_process');
const lines = [];
const ch = child_process.execFile('sh', ['test.sh'], () => {
console.log(lines.join(''));
});
ch.stdout.on('data', (data) => { lines.push(data); });
ch.stderr.on('data', (data) => { lines.push(data); });
产出:
line 1
line 3
line 2
line 4
我怎样才能获得相同顺序的行,并且知道哪些是来自stdout的,哪些是来自stderr的
注意,这里提到了一些关于缓冲的相互矛盾的事情。一方面他们说
默认情况下,stdin、stdout和stderr的管道在父Node.js进程和生成的子进程之间建立。这些管道的容量有限(且特定于平台)。如果子进程在未捕获输出的情况下写入stdout超过该限制,则子进程将阻止等待管道缓冲区接受更多数据
但是后来他们提到了一个设置,maxBuffer
,它说
maxBuffer在stdout或stderr上允许的最大数据量(字节)。如果超出,则终止子进程并截断任何输出
这两种解释似乎有冲突。我认为将maxBuffer
设置为0可能是解决方案,但显然不是
在任何情况下,尽管还不清楚如何解除这些流的缓冲
spawn
本身就有,但正在尝试
const child_process = require('child_process');
const lines = [];
const ch = child_process.spawn(process.argv[0], ['main.js']);
ch.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ch.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ch.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
结果是一样的
stdout: log: line1
stderr: error: line2
error: line4
stdout: log: line3
child process exited with code 0
我怀疑这与制作一个可写的
流并使用spawn
将其传入有关,但不清楚如何做到这一点。我试图创建一个可写的
并将其传入,但没有成功
internal/child_process.js:996
throw new ERR_INVALID_OPT_VALUE('stdio', inspect(stdio));
^
TypeError [ERR_INVALID_OPT_VALUE]: The value "Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 0,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: {
next: null,
entry: null,
finish: [Function: bound onCorkedFinish]
}
},
writable: true,
_write: [Function: write],
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
[Symbol(kCapture)]: false
}" is invalid for option "stdio"
在节点中生成子进程时,您知道如何同步或至少正确地交错捕获stdout和stderr吗
我想我要做的显然是不可能的。IIUC我需要在同一个流中传递stdout和stderr,以使它们正确地交错,一旦我这样做,我就失去了分辨输出来自何处的能力
但是,如果没有其他事情,我需要立即(无缓冲)得到结果。想象一下,我正在制作一个基于节点的终端并运行命令,该输出需要返回给我,以便我可以显示它(如基于文本的微调器),在那里,好像它是缓冲的(这就是我现在看到的),我将只获取卡盘中的输出。如果修改实际的子进程是一个选项,您可以在写入每一行后通过刷新来强制它同步记录
main.js
示例:
var logbuffer = require('log-buffer');
const log = msg => {
console.log(`log: ${msg}`);
logbuffer.flush();
}
const error = msg => {
console.error(`error: ${msg}`);
logbuffer.flush();
}
log('line1');
error('line2');
log('line3');
error('line4');
否则,如控制台的Node.js中所述,控制台实例被配置为写入process.stdout
和process.stderr
。以下详细描述了它们的同步/异步:
恐怕你所做的
const child_process = require('child_process');
const lines = [];
const ch = child_process.execFile('sh', ['test.sh'], () => {
console.log(lines.join(''));
});
ch.stdout.on('data', (data) => { lines.push(data); });
ch.stderr.on('data', (data) => { lines.push(data); });
这是我们能做到的最大限度。测试脚本的问题
#!/bin/sh
echo line 1
echo line 2 >&2
echo line 3
echo line 4 >&2
就是两个字符串“第1行\n”
和“第3行\n”
写得太快了
我的意思是:从子级到父级的数据传输由操作系统处理,操作系统(可能)使用低级管道或任何其他类型的缓冲区。即使孩子在未缓冲的情况下写入
stdout
会发生什么?子系统写入“第1行\n”
,操作系统将其缓冲在某个地方,并向父系统发送信号,该信号上升到最终需要读取的父系统;如果在这段时间内,子系统也写入“第3行\n”
,那么操作系统除了缓冲它(使用以前写入的数据)之外没有其他方法,因此当父系统最终读取数据时,无法知道写入数据时进行了多少次(无缓冲)写入。我在节点10.19.0
上运行,得到了1 2 3 4输出。你的节点版本是什么?我在节点12.18.0上进行MacOSE测试,你需要能够区分stderr和stdout吗?正如下面的评论所指出的,使用与stderr和stdout相同的文件可以获得您想要的顺序,但这意味着要写入文件,并且无法知道消息来自何处。是的,你需要知道。”out:file1’、‘error:fail’、‘out:file2’的含义与‘out:file1’、‘out-file2’、‘error:fail’的含义不同,这都是带有execFile的echo-line 2>&2的。感谢您的建议。我无法控制我正在生成的程序。它们可以是任何东西,rust
,clang
,make
,git
,等等。。。所以我不能增加它们来冲水。我不知道你链接的那些文档是如何帮助你的。有没有办法将stdout和stderr设置为虚拟文件,以便我可以跨平台同步查看输出?谢谢您的确认。由于子进程可能是任意的,我认为您所描述的无法实现。引用的文档旨在强调Node.jsstdout
和stderr
是独立的,并且根据各自的平台同步或异步运行。文档还说,如果传入文件,则所有平台都是同步的,因此我想知道是否有方法传入假/伪/虚拟文件这是无缓冲的,当生成的应用程序写入时,我可以从中读取
#!/bin/sh
echo line 1
echo line 2 >&2
echo line 3
echo line 4 >&2