【问题标题】:Parse output of spawned node.js child process line by line逐行解析生成的node.js子进程的输出
【发布时间】:2012-03-20 04:06:49
【问题描述】:

我有一个 PhantomJS/CasperJS 脚本,我使用 process.spawn() 在 node.js 脚本中运行它。由于 CasperJS 不支持 require()ing 模块,我正在尝试将命令从 CasperJS 打印到 stdout,然后使用 spawn.stdout.on('data', function(data) {}); 从我的 node.js 脚本中读取它们,以便执行向 redis 添加对象等操作/mongoose(很复杂,是的,但似乎比为此设置 Web 服务更简单......)CasperJS 脚本执行一系列命令并创建,例如,需要添加到我的数据库中的 20 个屏幕截图。

但是,我不知道如何将data 变量(Buffer?)分成几行...我尝试将其转换为字符串然后进行替换,我尝试过正在做spawn.stdout.setEncoding('utf8');,但似乎没有任何效果......

这是我现在拥有的

var spawn = require('child_process').spawn;

var bin = "casperjs"
//googlelinks.js is the example given at http://casperjs.org/#quickstart
var args = ['scripts/googlelinks.js'];
var cspr = spawn(bin, args);

//cspr.stdout.setEncoding('utf8');
cspr.stdout.on('data', function (data) {
    var buff = new Buffer(data);
    console.log("foo: " + buff.toString('utf8'));
});

cspr.stderr.on('data', function (data) {
    data += '';
    console.log(data.replace("\n", "\nstderr: "));
});

cspr.on('exit', function (code) {
    console.log('child process exited with code ' + code);
    process.exit(code);
});

https://gist.github.com/2131204

【问题讨论】:

  • 这是最好的方法吗?似乎 stdout.on('data') 事件根据缓冲区大小触发,不一定是新行。这是真的吗?

标签: node.js phantomjs


【解决方案1】:

试试这个:

cspr.stdout.setEncoding('utf8');
cspr.stdout.on('data', function(data) {
  var str = data.toString(), lines = str.split(/(\r?\n)/g);
  for (var i=0; i<lines.length; i++) {
    // Process the line, noting it might be incomplete.
  }
});

请注意,“数据”事件可能不一定会在输出行之间均匀中断,因此单行可能会跨越多个数据事件。

【讨论】:

  • 奇怪,我在 OSX 上 - 我以为“\r\n”是 Windows。但它似乎工作! (在添加了一些缺少的括号之后:p)
  • @JesseFulton:\r 是正则表达式特殊字符 ? 中的可选参数,因此此代码应适用于 UNIX 和 Windows;它使正则表达式全局化(.../g),这在这里可能很关键。在您的示例代码中对“替换”的调用使用了一个普通字符串,该字符串被转换为非全局正则表达式,因此您可能只得到两行而不是全部。
  • 啊,是的,你是对的。 String.replace(String, String) 不是全局的 - 您需要使用正则表达式作为第一个参数并添加 'g' 开关。
  • @mehaase:哪条评论?
  • 虽然此解决方案有时有效,但我希望看到始终有效的解决方案。
【解决方案2】:

我实际上为此目的编写了一个 Node 库,它被称为 stream-splitter,你可以在 Github 上找到它:samcday/stream-splitter

该库提供了一个特殊的Stream,您可以将您的 casper 标准输出与分隔符(在您的情况下为 \n)一起通过管道传输,它会发出整洁的 token 事件,它对应于它拆分的每一行来自输入Stream。其内部实现非常简单,并将大部分魔法委托给substack/node-buffers,这意味着没有不必要的Buffer 分配/副本。

【讨论】:

  • 这个库在这种特殊情况下可以节省大量时间。谢谢!
  • +1 这对我有用。麻烦都过去了。谢谢!
  • 与 child_process 一起使用: var splitter = proc.stdout.pipe(StreamSplitter('\n')); splitter.on('token', (token) => { console.log(token) }; // 谢谢!
【解决方案3】:

我发现了一个更好的方法,只使用纯节点,这似乎工作得很好:

const childProcess = require('child_process');
const readline = require('readline');

const cspr = childProcess.spawn(bin, args);

const rl = readline.createInterface({ input: cspr.stdout });
rl.on('line', line => /* handle line here */)

【讨论】:

    【解决方案4】:

    添加到 maerics 的答案,它不能正确处理仅在数据转储中馈送行的一部分的情况(他们将分别为您提供该行的第一部分和第二部分,作为两条单独的行。 )

    var _breakOffFirstLine = /\r?\n/
    function filterStdoutDataDumpsToTextLines(callback){ //returns a function that takes chunks of stdin data, aggregates it, and passes lines one by one through to callback, all as soon as it gets them.
        var acc = ''
        return function(data){
            var splitted = data.toString().split(_breakOffFirstLine)
            var inTactLines = splitted.slice(0, splitted.length-1)
            var inTactLines[0] = acc+inTactLines[0] //if there was a partial, unended line in the previous dump, it is completed by the first section.
            acc = splitted[splitted.length-1] //if there is a partial, unended line in this dump, store it to be completed by the next (we assume there will be a terminating newline at some point. This is, generally, a safe assumption.)
            for(var i=0; i<inTactLines.length; ++i){
                callback(inTactLines[i])
            }
        }
    }
    

    用法:

    process.stdout.on('data', filterStdoutDataDumpsToTextLines(function(line){
        //each time this inner function is called, you will be getting a single, complete line of the stdout ^^
    }) )
    

    【讨论】:

      【解决方案5】:

      你可以试试这个。它将忽略任何空行或空的新换行符。

      cspr.stdout.on('data', (data) => {
          data = data.toString().split(/(\r?\n)/g);
          data.forEach((item, index) => {
              if (data[index] !== '\n' && data[index] !== '') {
                  console.log(data[index]);
              }
          });
      });
      

      【讨论】:

        【解决方案6】:

        旧东西,但仍然有用...

        为此,我制作了一个自定义流 Transform 子类。

        https://stackoverflow.com/a/59400367/4861714

        【讨论】:

          猜你喜欢
          • 2012-01-24
          • 1970-01-01
          • 2014-02-13
          • 2012-12-29
          • 2011-02-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多