【问题标题】:Node.js: Easiest way to capture stdout into string variableNode.js:将标准输出捕获到字符串变量中的最简单方法
【发布时间】:2018-10-17 22:01:16
【问题描述】:

我有以下代码:

const spawn = require("child_process").spawn;

const netStat = spawn('netstat', ['-nat']);
const grepPort = spawn('grep', ['3000']);
const grepStatus = spawn('grep', ['ESTABLISHED']);

console.log('Determining public ip\'s connected to port 3000');

// get port 3000 from netstat
netStat.stdout.on('data', (data) => {
  grepPort.stdin.write(data);
  });

netStat.on('close', (code) => {
  if (code !== 0) {console.log(`netstat process exited with code ${code}`);}
  grepPort.stdin.end();
});

// get ESTABLISHED from port 3000
grepPort.stdout.on('data', (data) => {
  grepStatus.stdin.write(data);
  });

grepPort.on('close', (code) => {
  grepStatus.stdin.end();
});

grepStatus.stdout.on('data', (data) => {
  console.log(data.toString())
  // Store data.toString() to variable here
});

我正在尝试将最终数据捕获到一个字符串中,其中注释位于第三行到最后一行。

我研究了流、缓冲区对象等,但没有任何工作(也许我做错了?)所以我正在寻找一个新的解决方案,或者一个简单的“它不可能”。

编辑: 假设代码被修改:

const spawn = require("child_process").spawn;
var port = 3000
console.log(`Determining public ip\'s connected to port ${port}`);

let output = '';

const proc = spawn('sh', [
  '-c',
  `netstat -nat | grep ${port}.*ESTABLISHED | awk '{print $5}' | grep -v .*${port}`
]);

proc.stdout.on('data', (chunk) => {
  output += chunk.toString();
});

proc.stderr.on('data', (chunk) => {
  console.log(chunk.toString());
});

proc.on('error', (err) => {
  console.error(err);
});

proc.on('exit', (code) => {
  console.log("Done");
});

console.log(output); // Should print output, no?

提前感谢您的帮助!

【问题讨论】:

    标签: node.js stream console global-variables stdout


    【解决方案1】:

    您可以将所有数据事件块附加到一个字符串上,并在管道结束时对其进行处理,如下所示:

    const spawn = require('child_process').spawn;
    const proc = spawn('netstat', ['-nat']);
    
    let output = '';
    proc.stdout.on('data', (chunk) => {
      output += chunk.toString();
    });
    proc.on('exit', () => {
      console.log(output);
    });
    

    output 以空字符串开始,每次出现data 事件(通常不止一次)时,您都可以将数据附加到该字符串,直到整个过程为exited。一旦处理exits,您将获得stdout 的总输出。

    同样对于您的脚本,使用单个 spawn 调用和 sh 命令将所有这些与 node.js 之外的 unix 管道连接起来可能更容易。结合上面的内容看起来像这样:

    const spawn = require("child_process").spawn;
    
    console.log('Determining public ip\'s connected to port 3000');
    
    let output = '';
    
    const proc = spawn('sh', [
      '-c',
      'netstat -nat | grep 3000 | grep ESTABLISHED'
    ]);
    
    proc.stdout.on('data', (chunk) => {
      output += chunk.toString();
    });
    
    proc.stderr.on('data', (chunk) => {
      console.log(chunk.toString());
    });
    
    proc.on('error', (err) => {
      console.error(err);
    });
    
    proc.on('exit', (code) => {
      console.log(code);
      console.log(output);
    });
    

    如果您仍然想要单独的 spawns,使用内置的 .pipe 方法将它们连接在一起可能更容易/有帮助:

    const spawn = require("child_process").spawn;
    
    console.log('Determining public ip\'s connected to port 3000');
    
    let output = '';
    
    const netStat = spawn('netstat', ['-nat']);
    const grepPort = spawn('grep', ['3000']);
    netStat.stdout.pipe(grepPort.stdin);
    const grepStatus = spawn('grep', ['ESTABLISHED']);
    grepPort.stdout.pipe(grepStatus.stdin);
    
    grepPort.stdout.on('data', (chunk) => {
      output += chunk.toString();
    });
    
    netstat.stderr.on('data', (chunk) => {
      console.log(chunk.toString());
    });
    
    grepPort.on('error', (err) => {
      console.error(err);
    });
    
    grepPort.on('exit', (code) => {
      console.log(code);
      console.log(output);
    });
    

    我在本地发现此错误很容易,因为该进程可能会在管道连接之前退出(例如,grep 喜欢被告知它应该用grep -f - 之类的东西收听stdin,否则它会以状态码 1 退出)。此外,每个管道都可以有 stderr data 您应该收听的事件。这是一个需要处理的问题,因此所有管道一起可能会有所帮助。

    如果 grep 行可以在您的系统上运行,grep '3000.*ESTABLISHED' 而不是两个 grep 调用会更简单。

    【讨论】:

    • 感谢您的回答!我也更喜欢您的第一个解决方案!至于output,它似乎是全局声明的,为什么必须在proc.on('exit', ... 块中才能打印到控制台等?
    • 最终我想全局存储变量以用于其他功能 - 而不仅仅是打印到控制台
    • exit 事件是最后触发的事件。到该事件触发时,您可以确定不会有更多的输出来自标准输出。此时,您可以调用另一个函数并使用整个输出,确保它是完整的输出,而不仅仅是部分输出。这有意义吗?
    • 是的,假设我理解正确,这对我来说很有意义。因此,我在原始问题编辑下的代码修订应该打印输出,对吗?
    • 它不会因为节点会首先运行你所有的代码(包括最后一行),然后(在事件循环中)稍后会触发一些 data 事件,然后(在事件循环)稍后,exit 事件将触发。这些事件之间的时间是可变的。因为event 处理程序是异步的,而底部的console.log 是同步的,所以同步代码首先执行,异步事件处理程序在几毫秒后执行。
    猜你喜欢
    • 2019-04-10
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多