Node.js starts a child process and receives terminal output in real time

I have a script that displays hi, sleeps for a second, issues hi, sleeps for 1 second, etc. etc. Now I thought that I could solve this problem with this model.

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

Now the problem is that the task must be completed to display the output. As I understand it, this is due to the fact that the newly created process requires execution control. Obviously node.js does not support threads, so any solutions? My idea was to run two instances, first for the specific purpose of creating the task, and get her to handle the output for the process of the second instance, given that this could be achieved.

+78
spawn
Jan 15 '13 at 7:09
source share
7 answers

I'm still getting wet with Node.js, but I have a few ideas. firstly, I believe that you need to use execFile instead of spawn ; execFile is for execFile when you have a script path, while spawn is for executing a known command that Node.js can resolve for your system path.

1. Provide a callback to handle buffered output:

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

2. Add a listener to the stdout stream of the child process ( 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()); }); 

In addition, there are options with which you can disconnect the generated process from the Node control terminal, which will allow it to work asynchronously. I have not tested this yet, but there are examples in the API documentation that look something like this:

 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 event loop from the parent's: child.unref(); child.stdout.on('data', function(data) { console.log(data.toString()); }); 
+70
Mar 08 '13 at 3:45
source share

Now much easier (6 years later)!

Spawn returns a childObject with which you can listen for events . Developments:

  • Class: ChildProcess
    • Event: Error
    • Event: Exit
    • Event: Close
    • Event: Disable
    • Event: Message

There are also several objects from childObject , they are:

  • Class: ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • child.connected
    • child.kill ([signal])
    • child.send (message [, sendHandle] [, callback])
    • child.disconnect ()

See here for more information on childObject: https://nodejs.org/api/child_process.html.

Asynchronous

If you want to start your process in the background while the node can still continue execution, use the asynchronous method. You can still perform actions after the process is completed and if there are any results.

child_process.spawn (...); (Node v0.1.90)

 var spawn = require('child_process').spawn; var child = spawn('node ./commands/server.js'); // You can also use a variable to save the output // for when the script closes later var scriptOutput = ""; child.stdout.setEncoding('utf8'); child.stdout.on('data', function(data) { //Here is where the output goes console.log('stdout: ' + data); data=data.toString(); scriptOutput+=data; }); child.stderr.setEncoding('utf8'); child.stderr.on('data', function(data) { //Here is where the error output goes console.log('stderr: ' + data); data=data.toString(); scriptOutput+=data; }); child.on('close', function(code) { //Here you can get the exit code of the script console.log('closing code: ' + code); console.log('Full output of script: ',scriptOutput); }); 

Here's how you would use the callback + asynchronous method :

 var child_process = require('child_process'); console.log("Node Version: ", process.version); run_script("ls", ["-l", "/home"], function(output, exit_code) { console.log("Process Finished."); console.log('closing code: ' + exit_code); console.log('Full output of script: ',output); }); console.log ("Continuing to do node things while the process runs at the same time..."); // This function will output the lines from the script // AS is runs, AND will return the full combined output // as well as exit code when it done (using the callback). function run_script(command, args, callback) { console.log("Starting Process."); var child = child_process.spawn(command, args); var scriptOutput = ""; child.stdout.setEncoding('utf8'); child.stdout.on('data', function(data) { console.log('stdout: ' + data); data=data.toString(); scriptOutput+=data; }); child.stderr.setEncoding('utf8'); child.stderr.on('data', function(data) { console.log('stderr: ' + data); data=data.toString(); scriptOutput+=data; }); child.on('close', function(code) { callback(scriptOutput,code); }); } 

Using the method described above, you can send each line of script output to the client (for example, using Socket.io to send each line when you receive events on stdout or stderr ).

synchronous

If you want the node to stop working and wait for the script to complete , you can use the synchronous version:

child_process.spawnSync (...); (Node v0.11. 12+)

Problems with this method:

  • If the script takes some time, your server will freeze during this time!
  • Stdout will be returned only after the script is finished . Since it is synchronous, it cannot continue until the current line ends. Therefore, he cannot capture the 'stdout' event until the spawn line ends.

How to use it:

 var child_process = require('child_process'); var child = child_process.spawnSync("ls", ["-l", "/home"], { encoding : 'utf8' }); console.log("Process finished."); if(child.error) { console.log("ERROR: ",child.error); } console.log("stdout: ",child.stdout); console.log("stderr: ",child.stderr); console.log("exist code: ",child.status); 
+90
Sep 30 '15 at 17:57
source share

Here is the cleanest approach I've found:

 require("child_process").spawn('bash', ['./script.sh'], { cwd: process.cwd(), detached: true, stdio: "inherit" }); 
+18
Nov 16 '17 at 20:26
source share

I had a little problem getting the output log from the "npm install" command when I spawned npm in a child process. Real-time registration of dependencies was not displayed in the parent console.

The easiest way to do what the original poster wants is to create (npm on windows and put everything in the parent console):

 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'); }); 
+13
Feb 05 '16 at 2:05
source share

child:

 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 stdio 
+1
Aug 13 '13 at 2:14
source share

I have often required this function to pack it into the std-pour library. It should allow you to execute the command and view the output in real time. Just install:

 npm install std-pour 

Then just execute the command and see the result in real time:

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

He promised that you can link several teams. It even works with a signature compatible with child_process.spawn , so it should be a replacement anywhere you use it.

+1
Jun 17 '17 at 18:20
source share

Adding an answer related to child_process.exec as I also needed live feedback, and I did not get it until the script ended. This also complements my comment on the accepted answer, but as it is formatted, it will be a little more understandable and easier to read.

Essentially, I have an npm script that invokes Gulp, invoking a task that subsequently uses child_process.exec to execute a bash or batch script, depending on the OS. Any of the scripts starts the build process through Gulp, and then makes several calls to some binaries that work with Gulp output.

This is exactly the same as others (spawn, etc.), but for completeness, here's how to do it exactly:

 // 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 (ie 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 } ); 

Now it's as easy as adding an event listener. For standard stdout :

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

And for stderr :

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

Not bad at all - HTH

0
Jun 08 '18 at 14:45
source share



All Articles