在 NodeJS 中,我希望能够捕获内存中生成进程的输出(无需写入文件,也不使用继承的 sysout)。
文档说我可以使用
Stream
...
对象:与子进程共享引用 tty、文件、套接字或管道的可读或可写流。流的底层文件描述符在子进程中复制到与 stdio 数组中的索引相对应的 fd。流必须有一个底层描述符(文件流在“打开”事件发生之前不会启动)。
...但是,当我传递自定义流时,我收到错误:
The argument 'stdio' is invalid. Received CustomWritableStream
我怀疑我需要一个底层描述符 - 但我不知道这意味着什么,或者如何实现它。
如何让我的捕获流正常工作? (这可能吗?)
代码片段:
class CustomWritableStream extends Writable {
captureBuffer: string = '';
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void) {
const chunkStr = chunk.toString();
this.captureBuffer += chunkStr;
callback();
}
}
const stdout = new CustomWritableStream()
const stderr = new CustomWritableStream()
const child = child_process.spawn(cmd, { ...opts, stdio: ['ignore', stdout, stderr] })
类似:
如何使用 Node.js 捕获内存中生成进程的输出,而不写入文件或使用继承的 stdout。我们将介绍两种方法:使用自定义流和使用专用类来捕获流程输出。
在 Node.js 中使用子进程时,您可能需要在内存中捕获它们的输出(stdout 和 stderr)。这对于各种场景都很有用,例如运行命令行工具并以编程方式处理其输出。
在 Node.js 中,流是用于处理流数据的抽象接口。在处理子进程及其输出时,理解“底层描述符”的概念非常重要。
底层描述符是流用于读取或写入的低级 I/O 机制。对于文件流来说,这个描述符就是文件描述符。对于进程流(如 stdout 和 stderr),它是一个管道。
生成子进程时,Node.js 需要正确设置这些底层描述符以捕获输出。这就是为什么简单地将自定义流传递给
child_process.spawn()
并不能开箱即用——自定义流没有 Node.js 可以使用的底层描述符。
捕获输出的一种方法是创建将数据存储在内存中的自定义流。我们将实现两种类型的流:a
CustomWritableStream
和 CustomDuplexStream
。
const { Writable } = require('stream');
class CustomWritableStream extends Writable {
constructor(options) {
super(options);
this.captureBuffer = '';
}
_write(chunk, encoding, callback) {
const chunkStr = chunk.toString();
this.captureBuffer += chunkStr;
callback();
}
getContents() {
return this.captureBuffer;
}
}
const { Duplex } = require('stream');
class CustomDuplexStream extends Duplex {
constructor(options) {
super(options);
this.captureBuffer = '';
}
_write(chunk, encoding, callback) {
const chunkStr = chunk.toString();
this.captureBuffer += chunkStr;
callback();
}
_read(size) {
// This method is required for Duplex streams, but we don't need to implement it
// since we're not reading from this stream
}
getContents() {
return this.captureBuffer;
}
}
这些自定义流将传入数据存储在
captureBuffer
字符串中。 getContents()
方法允许您检索捕获的输出。
另一种方法是创建一个专用类来捕获流程输出。此类使用 Node.js 的内置流和事件来捕获输出。
const { spawn } = require('child_process');
class ProcessOutputCapture {
constructor() {
this.stdoutBuffer = '';
this.stderrBuffer = '';
}
captureOutput(command, args = []) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
child.stdout.on('data', (data) => {
this.stdoutBuffer += data.toString();
});
child.stderr.on('data', (data) => {
this.stderrBuffer += data.toString();
});
child.on('close', (code) => {
resolve({
stdout: this.stdoutBuffer,
stderr: this.stderrBuffer,
exitCode: code
});
});
child.on('error', (error) => {
reject(error);
});
});
}
getStdout() {
return this.stdoutBuffer;
}
getStderr() {
return this.stderrBuffer;
}
clearBuffers() {
this.stdoutBuffer = '';
this.stderrBuffer = '';
}
}
此类提供了捕获输出、分别检索 stdout 和 stderr 以及清除缓冲区的方法。
const capture = new ProcessOutputCapture();
capture.captureOutput('ls', ['-l'])
.then(result => {
console.log('Stdout:', result.stdout);
console.log('Stderr:', result.stderr);
console.log('Exit code:', result.exitCode);
})
.catch(error => {
console.error('Error:', error);
});
这两种方法都提供了使用 Node.js 捕获内存中生成进程的输出的有效方法。自定义流方法提供了更大的灵活性,可以在各种场景中使用,而
ProcessOutputCapture
类提供了更简单、更集中的解决方案来捕获流程输出。
选择最适合您的特定用例和编码风格的方法。请记住在实施中适当处理错误和边缘情况。