2012-03-19 20 views
43

Estoy tratando de ejecutar un comando de shell desde Node.js, sin redireccionando la entrada y salida de ese comando - al igual que desgranar un comando usando un script de shell , o usando el comando system de Ruby. Si el proceso hijo quiere escribir en STDOUT, quiero que vaya directamente a la consola (o que me redirijan, si la salida de mi aplicación Node fue redirigida).Ejecutando un comando de shell desde Node.js sin buffering salida

El nodo no parece tener ninguna forma directa de hacerlo. Parece que la única forma de ejecutar otro proceso es con child_process, que siempre redirige la entrada y salida del proceso secundario a las tuberías. Puedo escribir código para aceptar datos de esos tubos y escribirlos en STDOUT y STDERR de mi proceso, pero si hago eso, las API me obligan a sacrificar algo de flexibilidad.

Quiero dos características:

  • sintaxis Shell. Quiero poder canalizar la salida entre comandos o ejecutar archivos por lotes de Windows.
  • Salida ilimitada. Si estoy desembolsando un compilador y quiere generar megabytes de advertencias de compilación, quiero que todos se desplacen por la pantalla (hasta que el usuario se sienta cansado y presione Ctrl + C).

Parece que Node quiere obligarme a elegir entre esas dos características.

  • Si quiero una cantidad ilimitada de salida, puedo usar child_process.spawn y luego hacer child.stdout.on('data', function(data) { process.stdout.write(data); }); y lo mismo para stderr, y va a felizmente datos del tubo hasta que las vacas vuelvan a casa. Desafortunadamente, spawn no es compatible con la sintaxis del shell.
  • Si quiero sintaxis de shell, puedo usar child_process.exec. Pero exec insiste en almacenar temporalmente el STDOUT y el STDERR del proceso secundario y dándome todo al final, y limita el tamaño de esos almacenamientos intermedios (configurable, 200K de forma predeterminada). Todavía puedo conectar los eventos on('data'), si deseo ver el resultado tal como se genera, pero exec aún agregará los datos a sus búferes también. Cuando la cantidad de datos excede el tamaño del búfer predefinido, exec finalizará el proceso hijo.

(También hay child_process.execFile, que es el peor de ambos mundos desde un punto de vista flexibilidad:. Ningún sintaxis del shell, pero todavía tiene que limitar la cantidad de producción que espera)

Me estoy perdiendo ¿alguna cosa? ¿Hay alguna manera de simplemente pagar en un proceso hijo en Node, y no redirigir su entrada y salida? Algo que admite la sintaxis de la shell y no causa una cantidad predefinida de salida, tal como está disponible en los scripts de shell, Ruby, etc.

+1

¿Alguna vez descubrió esto? Estoy en una situación similar. –

+0

mismo problema, 'hasta que las vacas vuelvan a casa' gracias, por eso, hice mi día después de las 10h de trabajo – neric

Respuesta

4

no he usado, pero he visto esta biblioteca: https://github.com/polotek/procstreams

Se le haría esto. El .out() canaliza automáticamente al stdin/out del proceso.

var $p = require('procstreams'); 
$p('cat lines.txt').pipe('wc -l').out(); 

Si no soporta la sintaxis del shell, pero eso es bastante trivial que pienso.

var command_str = "cat lines.txt | wc -l"; 
var cmds = command_str.split(/\s?\|\s?/); 
var cmd = $p(cmds.shift()); 
while(cmds.length) cmd = cmd.pipe(cmds.shift()); 
cmd 
    .out() 
    .on('exit', function() { 
    // Do whatever 
    }); 
+3

Lo probé y no es compatible con los archivos por lotes de Windows, que es realmente la razón principal por la que deseo compatibilidad con shell. Muchas herramientas originales de Unix (por ejemplo, Rake) se implementan usando archivos por lotes en Windows, y quiero poder invocarlos desde Node. –

+0

¿Qué quiere decir con "no admite"? Se bloquea, dice que el archivo por lotes no se encuentra, se bloquea y se ejecuta para siempre? – loganfsmyth

+0

'CreateProcessW: El sistema no puede encontrar el archivo especificado. Mismo error que' child_process.spawn' cuando intenta ejecutar un archivo por lotes, porque los archivos por lotes no son ejecutables (directamente); si trato de ejecutar '$ p ('rake'). out();', va en busca de un ejecutable binario llamado 'rake.exe' o' rake.com', no los encuentra y los errores salen. Necesita el shell ('cmd.exe') si desea ejecutar archivos por lotes. –

37

Puede heredar flujos stdin/salida/error a través spawn argumento por lo que no necesita tubería de forma manual:

var spawn = require('child_process').spawn; 
spawn('ls', [], { stdio: 'inherit' }); 

Uso cáscara para la sintaxis del shell - de fiesta que es -c parámetro para leer guión de cadena:

var spawn = require('child_process').spawn; 
var shellSyntaxCommand = 'ls -l | grep test | wc -c'; 
spawn('sh', ['-c', shellSyntaxCommand], { stdio: 'inherit' }); 

Para resumir:

var spawn = require('child_process').spawn; 
function shspawn(command) { 
    spawn('sh', ['-c', command], { stdio: 'inherit' }); 
} 

shspawn('ls -l | grep test | wc -c'); 
+0

Eso es un poco más simple que tener que canalizar la salida manualmente, pero todavía no admite la sintaxis del shell. Claro, puedo [elegir manualmente un intérprete de comandos para invocar] (http://stackoverflow.com/a/9779382/87399), pero entonces mi código no es multiplataforma. –

+0

Eso '{stdio: 'inherit'}' realmente funcionó. Gracias – kabirbaidhya

+0

si quiere obtener stdout y stderr, reemplace 'inherit' por 'pipe' – Guig

-1

Hay un ejemplo en el node docs para el módulo child_process:

Ejemplo de supresión de un proceso de larga duración y redirigir su salida a un archivo:

var fs = require('fs'), 
    spawn = require('child_process').spawn, 
    out = fs.openSync('./out.log', 'a'), 
    err = fs.openSync('./out.log', 'a'); 

var child = spawn('prg', [], { 
    detached: true, 
    stdio: [ 'ignore', out, err ] 
}); 

child.unref(); 
+2

¿Cómo responde esto la pregunta? Está mostrando cómo redirigir la salida a un archivo; la pregunta era muy específicamente sobre * no * redirigir la salida. –

+0

Creo que tienes razón. Obviamente, leí mal la pregunta la primera vez. Me disculpo. – darelf

+1

Esto me ayudó, gracias amigo. – Chance

3

Puede reemplazar exec por spawn y el uso la sintaxis del shell simplemente con:

const {spawn} = require ('child_process'); 
const cmd = 'ls -l | grep test | wc -c'; 
const p = spawn (cmd, [], {shell: true}); 

p.stdout.on ('data', (data) => { 
    console.log (data.toString()); 
}); 

la magia es sólo {shell: true}.

Cuestiones relacionadas