node.js - español - El nodo genera múltiples problemas en el proceso de shell
comandos cluster linux (0)
Desarrollé un módulo para ejecutar un comando genérico utilizando child_process
spawn
. Tengo dos api en este módulo: una sola ejecución de comandos api, CommandLine.execute
y una ejecución de comandos múltiples api, CommandLine.executeCommands
, que llamo de esta manera:
// commandItemsArray is a list of commands list
// that is an array of [command, options, arguments]
commandItemsArray = [ [''ls'',''-l'',''./''], [''ls'',''-a'',''./''] ];
command.executeCommands( commandItemsArray
, function(results) {
console.log( results );
}
, function(error) {
console.log( error );
});
o
commandItemsArray = [ [''ls'',''-l'',''./''] ];
command.executeCommands( commandItemsArray
, function(results) {
console.log( results );
}
, function(error) {
console.log( error );
});
Este módulo es independiente (depende solo de los módulos incorporados en el nodo) y ese es el código:
(function() {
var CommandLine;
CommandLine = (function() {
var cp = require(''child_process'');
/**
* Command line helper module
* This module is standalone
* @author: loretoparisi at gmail dot com
*/
function CommandLine(options,logger) {
var self=this;
// defaults
this._options = {
// true to debug
debug : false,
// true to show errors
error : true
};
// override defaults
for (var attrname in options) { this._options[attrname] = options[attrname]; }
/**
* Wrappar around Promise.all
*/
this.PromiseAll = function(items, block, done, fail) {
var self = this;
var promises = [],
index = 0;
items.forEach(function(item) {
promises.push(function(item, i) {
return new Promise(function(resolve, reject) {
if (block) {
block.apply(this, [item, index, resolve, reject]);
}
});
}(item, ++index))
});
Promise.all(promises).then(function AcceptHandler(results) {
if (done) done(results);
}, function ErrorHandler(error) {
if (fail) fail(error);
});
}; //PromiseAll
}
/**
* Execute a command
* @param commands Array of command, options, arguments
* @example [''ls'',''-l'',''./'']
* @param resolve Block success block
* @param reject Block rejection block
*/
CommandLine.prototype.execute = function(command, resolve, reject) {
var self=this;
resolve = resolve || function(results) {};
reject = reject || function(error) {};
return self.ExecutionBlock(item, index, resolve, reject);
} //execute
/**
* Execute a list of commands
* @param commands Array of command array of of command, options, arguments
* @example [ [''ls'',''-l'',''./''], [''ls'', ''-a'', ''./''] ]
* @param resolve Block success block
* @param reject Block rejection block
*/
CommandLine.prototype.executeCommands = function(commands, resolve, reject) {
var self=this;
resolve = resolve || function(results) {};
reject = reject || function(error) {};
/**
* Execution block handler
*/
var ExecutionBlock = function(item, index, resolve, reject) {
var executable = item[0]; // first elem is command
var options = item.splice(1,item.length);
if(self._options.debug) {
console.log( item );
console.log( executable, options.join('' '') );
}
var data = new Buffer("",''utf-8'');
// LP: now spawn the power!
var child = cp.spawn(executable, options);
// Listen for an exit event:
child.on(''exit'', function(exitCode) {
return resolve( { data : data.toString(''utf-8''), code : exitCode } );
});
// Listen for stdout data
child.stdout.on(''data'', function(_data) {
console.log( ( new Buffer(_data)).toString() );
data = Buffer.concat([data, _data]);
});
// child error
child.stderr.on(''data'',
function(data) {
if(self._options.error) {
console.log(''err data: '' + data);
}
// on error, kill this child
child.kill();
return reject(new Error(data.toString()));
}
);
} //ExecutionBlock
self.PromiseAll(commands
, function(item, index, _resolve, _reject) {
ExecutionBlock(item, index, _resolve, _reject);
}
, function(results) { // aggregated results
// all execution done here. The process exitCodes will be returned
// array index is the index of the processed that exited
return resolve(results);
}
, function(error) { // error
return reject(error);
});
} //executeCommands
return CommandLine;
})();
module.exports = CommandLine;
}).call(this);
Estoy usando Promise.all
para generar múltiples procesos, esperar la ejecución y recopilar salida stdout
en un Buffer
nodo como:
// Listen for stdout data
child.stdout.on(''data'', function(_data) {
console.log( ( new Buffer(_data)).toString() );
data = Buffer.concat([data, _data]);
});
En el evento hijo de exit
muy, devuelvo el código de salida y los datos del proceso secundario en una estructura:
child.on(''exit'', function(exitCode) {
return resolve( { data : data.toString(''utf-8''), code : exitCode } );
});
Si bien, la ejecución de algunos comandos no tiene ningún problema, la ejecución de comandos del shell como ls
me devuelve resultados inesperados, como que no obtengo los resultados esperados en la devolución de llamada del método PromiseAll
, y no entiendo por qué.
self.PromiseAll(commands
, function(item, index, _resolve, _reject) {
ExecutionBlock(item, index, _resolve, _reject);
}
, function(results) { // aggregated results
// all execution done here. The process exitCodes will be returned
// array index is the index of the processed that exited
return resolve(results);
}
, function(error) { // error
return reject(error);
});
Una duda es acerca de la concatenación de Buffer
nodo explicada aquí , que parece ser difícil:
var data = new Buffer("",''utf-8'');
// LP: now spawn the power!
var child = cp.spawn(executable, options);
// Listen for an exit event:
child.on(''exit'', function(exitCode) {
return resolve( { data : data.toString(''utf-8''), code : exitCode } );
});
// Listen for stdout data
child.stdout.on(''data'', function(_data) {
console.log( ( new Buffer(_data)).toString() );
data = Buffer.concat([data, _data]);
});
{ data : data.toString(''utf-8'')}
mantener el buffer entero encadenado como cadena, pero me pregunto si el utf-8
es la codificación correcta para la salida stdout
, se supone que estamos hablando de comandos de shell por supuesto.