web-dev-qa-db-fra.com

Analyser la sortie ligne par ligne du processus enfant engendré par node.js

J'ai un script PhantomJS/CasperJS que je lance à partir d'un script node.js à l'aide de process.spawn(). Étant donné que CasperJS ne prend pas en charge les modules require()ing, j'essaie d'imprimer des commandes de CasperJS vers stdout, puis de les lire dans mon script node.js à l'aide de spawn.stdout.on('data', function(data) {}); afin d'effectuer des opérations telles que l'ajout d'objets à redis/mongoose (alambiqué, oui, mais cela semble plus simple que de configurer un service Web pour cela ...) Le script CasperJS exécute une série de commandes et crée, par exemple, 20 captures d'écran qui doivent être ajoutées à ma base de données.

Cependant, je ne vois pas comment diviser la variable data (une Buffer?) En lignes ... J'ai essayé de la convertir en chaîne, puis de la remplacer. J'ai essayé de faire spawn.stdout.setEncoding('utf8'); mais rien ne semble fonctionner. ...

Voici ce que j'ai en ce moment

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

19
Jesse Fulton

Essaye ça:

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

Notez que l'événement "data" peut ne pas nécessairement se répartir uniformément entre les lignes de sortie, de sorte qu'une seule ligne peut s'étendre sur plusieurs événements de données.

14
maerics

J'ai en fait écrit une bibliothèque de nœuds dans ce but, elle s'appelle stream-splitter et vous pouvez la trouver sur Github: samcday/stream-splitter .

La bibliothèque fournit une Stream spéciale dans laquelle vous pouvez diriger votre casper stdout, ainsi qu'un délimiteur (dans votre cas,\n), et elle émettra de jolis événements token, un pour chaque ligne séparée de l'entrée Stream. L'implémentation interne est très simple et permet de déléguer l'essentiel de la magie à substack/node-buffers , ce qui signifie qu'il n'y a pas d'allocation/copie Buffer inutile.

12
Sam Day

Ajout à la réponse de maerics, qui ne traite pas correctement les cas où seule une partie d'une ligne est introduite dans un vidage de données (la première et la seconde partie de la ligne vous seront attribuées séparément, comme deux lignes distinctes).

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])
        }
    }
}

usage:

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 ^^
}) )
2
mako

J'ai trouvé un moyen plus agréable de faire cela avec un nœud pur, ce qui semble bien fonctionner:

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

1
nyctef

Vous pouvez essayer ceci. Il ignorera toutes les lignes vides ou les nouveaux sauts de ligne vides.

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]);
        }
    });
});
0
Rick