Node.js如何同步合并execFileSync/execFile的stdout与stderr到内存?
同步合并子进程stdout与stderr到Buffer(无需文件)
我尝试获取服务器上调用脚本的完整输出(stdout + stderr),但无法得到合并后的字符串。用fs.createWriteStream写入文件的方式可行:
可行的文件写入实现
test.sh
echo "Hello $1 I'm a stdout message" echo "Hello $1 I'm a stderr message" 1>&2
main.js
const {execFileSync} = require("node:child_process"); const fs = require("fs"); let writableStream = fs.createWriteStream("out"); writableStream.on('open', function() { execFileSync("./test.sh", ["UserName"], {stdio: ['ignore', writableStream, writableStream]}); });
但我无法将输出写入Buffer或直接通过.toString()获取,用Duplex流尝试时出现报错:
报错的Buffer尝试实现
main.js
const {execFileSync} = require("node:child_process"); const stream = require("node:stream"); let duplexStream = stream.Duplex(); let buffer = Buffer.alloc(10000); duplexStream.on('open', function() { execFileSync("./test.sh", ["UserName"], {stdio: ['ignore', duplexStream, duplexStream]}); }); duplexStream.pipe(buffer); console.log(buffer.toString());
报错信息
node:internal/streams/readable:680 dest.on('unpipe', onunpipe); ^ TypeError: dest.on is not a function at Duplex.Readable.pipe (node:internal/streams/readable:680:8) at Object.<anonymous> (/mnt/c/Users/-/Documents/temp/testRedirectStderr/main.js:9:14) at Module._compile (node:internal/modules/cjs/loader:1105:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/main/run_main:77:12) at node:internal/main/run_main_module:17:47
请问是否有无需使用文件,同步合并stdout与stderr(非简单拼接)的方法?
解决方案
方法1:自定义可写流收集输出
创建一个自定义可写流来累积所有输出数据,利用execFileSync的同步特性,执行完成后合并缓冲区即可:
const { execFileSync } = require("node:child_process"); const { Writable } = require("node:stream"); // 初始化数组存储输出片段 const outputChunks = []; // 创建收集流,将写入的chunk存入数组 const collectStream = new Writable({ write(chunk, _, callback) { outputChunks.push(chunk); callback(); } }); // 同步执行脚本,将stdout和stderr都导向收集流 execFileSync("./test.sh", ["UserName"], { stdio: ['ignore', collectStream, collectStream] }); // 合并所有片段并转为字符串 const combinedOutput = Buffer.concat(outputChunks).toString(); console.log(combinedOutput);
这种方式能严格保留stdout和stderr的输出顺序,完全在Node.js流层面处理,不依赖文件或shell。
方法2:利用Shell重定向简化实现
如果场景允许依赖shell功能,可以直接通过2>&1将stderr重定向到stdout,然后直接获取execFileSync的返回值:
const { execFileSync } = require("node:child_process"); // 通过bash命令合并stderr到stdout,直接获取字符串输出 const combinedOutput = execFileSync("bash", ["-c", "./test.sh UserName 2>&1"], { encoding: "utf8" }); console.log(combinedOutput);
报错原因说明
之前的Duplex流方案报错是因为:
- 直接创建的
stream.Duplex()未实现_read和_write核心方法,无法正常工作; pipe方法要求目标是流对象,而Buffer不是流,因此抛出dest.on is not a function错误。
内容的提问来源于stack exchange,提问作者Mathias Osterhagen
相关产品推荐
相关产品推荐

