web-dev-qa-db-fra.com

Node.js spawn processus enfant et obtenir la sortie du terminal en direct

J'ai un script qui affiche 'salut', dort une seconde, affiche 'salut', dort pendant 1 seconde, et ainsi de suite. Maintenant, je pensais pouvoir résoudre ce problème avec ce modèle.

var spawn = require('child_process').spawn,
temp    = spawn('PATH TO SCRIPT WITH THE ABOVE BEHAVIOUR');

temp.stdout.pipe(process.stdout);

Maintenant, le problème est que la tâche doit être terminée pour que le résultat soit affiché. Si je comprends bien, cela est dû au fait que le processus nouvellement créé prend le contrôle de l’exécution. Evidemment, node.js ne supporte pas les threads, donc des solutions? Mon idée était d’exécuter éventuellement deux instances, la première dans le but spécifique de créer la tâche et de la diriger vers la sortie du processus de la seconde instance, en tenant compte de ce résultat.

67
foklepoint

Je commence toujours à me mouiller avec Node.js, mais j'ai quelques idées. Tout d'abord, je pense que vous devez utiliser execFile au lieu de spawn; execFile est pour lorsque vous avez le chemin d'accès à un script, alors que spawn est pour l'exécution d'une commande connue que Node.js peut résoudre en fonction de votre chemin système. 

1. Fournissez un rappel pour traiter la sortie mise en mémoire tampon:

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], function(err, stdout, stderr) { 
    // Node.js will invoke this callback when the 
    console.log(stdout); 
});  

2. Ajoutez un écouteur au processus enfant 'stdout stream ( 9thport.net )

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3' ]); 
// use event hooks to provide a callback to execute when data are available: 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

En outre, il semble exister des options vous permettant de détacher le processus généré du terminal de contrôle de Node, ce qui lui permettrait de s'exécuter de manière asynchrone. Je n'ai pas encore testé cela, mais il y a des exemples dans la documentation de l'API qui ressemble à ceci: 

child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], { 
    // detachment and ignored stdin are the key here: 
    detached: true, 
    stdio: [ 'ignore', 1, 2 ]
}); 
// and unref() somehow disentangles the child's event loop from the parent's: 
child.unref(); 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});
68
RubyTuesdayDONO

C'est beaucoup plus facile maintenant (2 ans plus tard)!

Spawn renvoie un childObject, que vous pouvez ensuite écouter pour les événements avec. Les événements sont:

  • Classe: ChildProcess
    • Evénement: 'erreur'
    • Evénement: 'sortie'
    • Événement: 'fermer'
    • Evénement: 'déconnecter'
    • Evénement: 'message'

Il y a aussi un tas d'objets de ChildObject, ils sont:

  • Classe: ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • enfant.connecté
    • child.kill ([signal])
    • child.send (message [ sendHandle] [ rappel])
    • child.disconnect ()

Voir plus d'informations ici sur childObject: https://nodejs.org/api/child_process.html

Donc, en combinant tout cela, vous obtenez: 

var spawn = require('child_process').spawn;
var child = spawn('node ./commands/server.js');
child.stdout.on('data', function(data) {
    console.log('stdout: ' + data);
    //Here is where the output goes
});
child.stderr.on('data', function(data) {
    console.log('stderr: ' + data);
    //Here is where the error output goes
});
child.on('close', function(code) {
    console.log('closing code: ' + code);
    //Here you can get the exit code of the script
});

Et c'est comme ça que vous pouvez obtenir la sortie avec Node! 

82
Katie S

J'ai eu un peu de mal à obtenir la sortie de journalisation à partir de la commande "npm install" lorsque j'ai créé npm dans un processus enfant. La journalisation en temps réel des dépendances ne s'est pas affichée dans la console parente. 

Voici le moyen le plus simple de faire ce que l’affiche originale veut (spawn npm sous windows et enregistrez tout sur la console parent):

var args = ['install'];

var options = {
    stdio: 'inherit' //feed all child process logging into parent process
};

var childProcess = spawn('npm.cmd', args, options);
childProcess.on('close', function(code) {
    process.stdout.write('"npm install" finished with code ' + code + '\n');
});
11
agenaille

Voici l'approche la plus propre que j'ai trouvée:

require("child_process").spawn('bash', ['./script.sh'], {
  cwd: process.cwd(),
  detached: true,
  stdio: "inherit"
});
9
Harel Ashwal

enfant:

setInterval(function() {
    process.stdout.write("hi");
}, 1000); // or however else you want to run a timer

parent:

require('child_process').fork('./childfile.js');
// fork'd children use the parent's stdio
1
Sandro Pasquali

Ajouter une réponse liée à child_process.exec, car moi aussi j'avais besoin d'un retour d'informations en direct et je n'en recevais pas avant la fin du script. Cela complète également mon commentaire à la réponse acceptée, mais sa mise en forme sera un peu plus compréhensible et plus facile à lire.

En gros, j'ai un script npm qui appelle Gulp, invoquant une tâche qui utilise ensuite child_process.exec pour exécuter un script bash ou batch, en fonction du système d'exploitation. Chaque script exécute un processus de construction via Gulp, puis appelle certains fichiers binaires fonctionnant avec la sortie Gulp.

C'est exactement comme les autres (spawn, etc.), mais pour compléter, voici comment faire:

// INCLUDES
import * as childProcess from 'child_process'; // ES6 Syntax


// GLOBALS
let exec = childProcess.exec; // Or use 'var' for more proper 
                              // semantics, though 'let' is 
                              // true-to-scope


// Assign exec to a variable, or chain stdout at the end of the call
// to exec - the choice, yours (i.e. exec( ... ).stdout.on( ... ); )
let childProcess = exec
(
    './binary command -- --argument argumentValue',
    ( error, stdout, stderr ) =>
    {
        if( error )
        {
            // This won't show up until the process completes:
            console.log( '[ERROR]: "' + error.name + '" - ' + error.message );
            console.log( '[STACK]: ' + error.stack );

            console.log( stdout );
            console.log( stderr );
            callback();            // Gulp stuff
            return;
        }

        // Neither will this:
        console.log( stdout );
        console.log( stderr );
        callback();                // Gulp stuff
    }
);

Maintenant, c’est aussi simple que d’ajouter un écouteur d’événements. Pour stdout:

childProcess.stdout.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live':
        console.log( '[STDOUT]: ' + data );
    }
);

Et pour stderr:

childProcess.stderr.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live' too:
        console.log( '[STDERR]: ' + data );
    }
);

Pas si mal du tout - HTH

0
Rik

Je me suis souvent retrouvé à avoir besoin de cette fonctionnalité assez souvent pour la regrouper dans une bibliothèque appelée std-pour . Il devrait vous permettre d’exécuter une commande et d’afficher le résultat en temps réel. Pour installer simplement:

npm install std-pour

Ensuite, il est assez simple d'exécuter une commande et de voir le résultat en temps réel:

const { pour } = require('std-pour');
pour('ping', ['8.8.8.8', '-c', '4']).then(code => console.log(`Error Code: ${code}`));

C'est promis pour vous permettre de chaîner plusieurs commandes. Il est même compatible avec les signatures child_process.spawn et devrait donc être un remplacement immédiat où que vous l'utilisiez.

0
Joel B