23

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

但是,我不知道如何将data变量(a 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

4

6 回答 6

19

试试这个:

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.
  }
});

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

于 2012-03-20T04:20:09.023 回答
13

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

该库提供了一个特殊的Stream,您可以将您的 casper stdout 与分隔符(在您的情况下为 \n)一起通过管道传输,并且它将发出整洁token的事件,它对应于从 input 中分离出来的每一行Stream。内部实现非常简单,并将大部分魔法委托给子堆栈/节点缓冲区,这意味着没有不必要的Buffer分配/复制。

于 2012-07-07T07:06:07.070 回答
9

我找到了一个更好的方法来使用纯节点来做到这一点,这似乎运作良好:

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 */)

于 2019-02-11T22:49:14.550 回答
2

添加到 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 ^^
}) )
于 2015-05-09T06:38:52.453 回答
0

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

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]);
        }
    });
});
于 2016-11-07T02:35:35.917 回答
0

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

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

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

于 2019-12-18T22:20:14.437 回答