Wednesday, 3 May 2017

How to get stdout data back and forth from child process in Node.js

I'm running a command in node.js using child_process.spawn. This command has an interactive mode, so that can get the stdin to take input from the console and output results to stdout. I'm using this mode, to send commands to this child via the child.stdin.write( data+'\r\n' );.

  • I suppose to get the results in the callback child.stdout.on('data'), but it seems I'm not getting all data wrote by the command, that I can see in the bash stdout output, but not in the callback.

  • I would like a way to retrieve the data sent with child.stdin.write( data+'\r\n' ); in order to get the tuple/struct (data,result), since the execution is asynchronous.

For convenience, I have added a callback for the child.stdout.on('data') to get results outside that scope. My code looks like:

    var exec = function(name,command,params,options,callback) {
    var self=this;

    /** default ChildProcess options
     * @see https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
    */
    var _options = {
        // Prepare child to run independently of its parent process
        //detached: true
    };
    for (var attrname in options) { _options[attrname] = options[attrname]; }

    var created_time= ( new Date() ).toISOString();
    var task = require('child_process').spawn(command, params, _options);
    task.stdout.on('data', function(result) {
        var out=new Buffer(result,'utf-8').toString();
        callback(out);
    });
    task.on('error', function(error) {
        if(self.logger) self.logger.error("exec:%s pid:%d error:\n%@",name,task.pid,error);
    });
    task.on('uncaughtException', function (error) {
        if(self.logger) self.logger.error("exec:%s pid:%s uncaughtException:\n%@",name,task.pid,error);
    });
    task.stdout.on('end', function(data) {
        if(self.logger) self.logger.debug("exec:%s end.",name);
    });
    task.stderr.on('data', function(data) {
        if(self.logger) self.logger.debug("exec:%s stderr:%s",name,data);
    });
    task.on('close', (code, signal) => {
        if(self.logger) self.logger.warn('task:%s pid:%s terminated due to receipt of signal:%s',name,task.pid,signal);
    });
    task.on('exit', function(code) {
        if(self.logger) self.logger.debug("exec:%s exit.",name);
        if (code != 0) {
            var error=new Error();
            error.description= command + ' ' + params.join(' ');
            error.code=code;
            if(self.logger) self.logger.error("exec:%s pid:%d error:\n%@",name,task.pid,error);
        }
        // at exit explicitly kill exited task
        task.kill('SIGINT');
    });
    return task;
}//exec

// load child task
var load = function(model,callback) {
    var m=exec('fasttext','./fasttext', ['predict', model, '-'], {}, callback);
    m.stdout.pipe(process.stdout);
    return m;
}//load

// send data to child task
var send = function(task,data) {
    task.stdin.setEncoding('utf-8');
    task.stdin.write( data );
    task.stdin.write('\r\n');
}//send

I run the command like

var model=load(modelFile,
function(result) {
    console.log(result);
});
send(model,result);

NOTE.

In this specific case the command was fasttext a command line classification utility by Facebook Inc., that can run in interactive mode when called like ./fasttext predict filePath -.



via loretoparisi

No comments:

Post a Comment