JavaScript

Child Process in Node.Js

A child process is a process created by a parent process. Node allows us to run a system command within a child process and listen in on its input/output. This includes being able to pass arguments to the command, and even pipe the results of one command to another.

We can create a child process in four different ways:

  1. spawn
  2. fork
  3. exec
  4. execFile

Windows and Linux operating systems use different commands, the examples demonstrated here use Unix commands which may not work in a Windows OS.

We can overcome this problem by identifying the Operating System and alternate which command to execute. For example:

const {platform} = require('os'),
osType = platform();
let command;
if (osType == 'win32')
 command = 'cls';
else
 command = 'clear';

spawn

child_process.spawn (command[, argsArray][, optionsObject])

spawn is the most common function, it launches a command in a new process which accepts command arguments. Pipes are established between the parent application and the child process for stdin, stdout, and stderr. It is an efficient way to execute commands as it does not create a shell to execute the commands (not true for Windows, you must set shell option to true).

The child_process.stdout and child_process.stderr events captured on the child process, if no error occurs, the output from the command is transmitted to the stdout and a data event fires on the parent process. If an error occurs the error event fires and the child process exited with a code of 1, when no error occurs, the child process exits with a code of 0. The close event triggered when when child process exits.

In the following, we create a child process to call the dir command to print the directory contents.

spawn a child process in Linux OS

let {spawn} = require ('child_process'),
dir = spawn('dir');

dir.stdout.on('data', (data) => {
 console.log(data.toString());
})
dir.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
dir.on('close', (code) => {
 console.log('Process exit code: '+code);
})

spawn a child process in Windows OS

We’ll receive the ENOENT error when try to spawn commands in Windows. To prevent this error you must set the shell option to true, see following code:

let {spawn} = require ('child_process'),
dir = spawn('dir',[],{shell:true});

dir.stdout.on('data', (data) => {
 console.log(data.toString());
})
dir.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
dir.on('close', (code) => {
 console.log('Process exit code: '+code);
})

spawn a cross platform child process

const {spawn} = require('child_process'),
{platform} = require('os'),
osType = platform();

let command = 'ls',
       args = [],
    options = {};

if (osType == 'win32') {
 command = 'dir';
 options.shell = true;
}

dir = spawn(command,args,options);

dir.stdout.on('data', (data) => {
 console.log(data.toString());
})
dir.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
dir.on('close', (code) => {
 console.log('Process exit code: '+code);
})

exec

child_process.exec(command[, options][, callback])

It is useful for when you want to invoke a command, and you only care about the final result, not about accessing the data from a child’s stdio streams as they come.

The difference between spawn and exec is that the spawn method will return all the data as a stream, whereas the exec method returns data as a buffer.

//child_process.exec(command[, options][, callback])
const {exec} = require ('child_process');
let command = 'dir',
options = {encoding:'utf8'};

//exec(command,(err,stdout,stderr)=>{
exec(command,options,(err,stdout,stderr)=>{
 if (err){
  console.log(err);
  console.log(stderr);
 } else {
  console.log(stdout);
 }
});

Above code also can written as follows:

const {exec} = require ('child_process');
let command = 'dir',
options = {encoding:'utf8'},

dir = exec(command,options);
//dir = exec(command);

dir.stdout.on('data', (data) => {
 console.log(data);
})
dir.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
dir.on('close', (code) => {
 console.log('Process exit code: '+code);
})

execFile

child_process.execFile(file[, args][, options][, callback])

Use execFile if you want to execute a file without using a shell (apart from Windows where some files, such as .bat and .cmd, cannot be executed). The difference is that exec spawns a shell, whereas the execFile function does not.

For Linux based platforms, create a file, file.sh, and write the following code and save it:

#!/bin/sh
echo "Hello world"

Now execute this file using execFile method:

const {execFile} = require('child_process');

let file = 'file.sh',
args = [],
options = {};

execFile(file,args,options,(err,stdout,stderr)=>{
 if (err){
  console.log(err);
  console.log(stderr);
 } else {
  console.log(stdout);
 }
});

How to use execFile in Windows

On Windows bat and cmd files are not executable on their own without a terminal i.e. cmd, and therefore cannot be launched using child_process.execFile.

Instead of execFile we can use spawn and exec methods to run batch files on Windows os.

Create a file.bat file and write the following code and save:

echo Showing content of this dir
dir

Executing a bat file using Node child_process.spawn()

let {spawn} = require ('child_process'),
file = 'file.bat',
fileExec = spawn(file,[],{shell:true});

fileExec.stdout.on('data', (data) => {
 console.log(data.toString());
})
fileExec.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
fileExec.on('close', (code) => {
 console.log('Process exit code: '+code);
})

Executing a bat file using Node child_process.exec()

let {exec} = require ('child_process'),
file = 'file.bat',
fileExec = exec(file);

fileExec.stdout.on('data', (data) => {
 console.log(data.toString());
})
fileExec.stderr.on('data', (data) => {
 console.log('Error: '+data);
})
fileExec.on('close', (code) => {
 console.log('Process exit code: '+code);
})

fork

child_process.fork(modulePath[, args][, options])

It is a special way to create child processes of Node itself, with a special IPC channel built in, called fork. It is a variation of spawn, the difference is that it invokes a specified Node module/file with an established communication channel that allows messages to be passed back and forth between parent and child.

One use of child_process.fork() is to spin off functionality to completely separate Node instances. In next tutorial we’ll use fork to communicate with our File Walker module to improve performance by integrating it as a second Node instance.

The first argument passed to fork is a path to a Node module to execute. Like spawnthe fork returns a ChildProcess object, the major difference is IPC channel:

  • the child process has a child.send(message) function
  • and the script being invoked by fork can listen for process on('message') events.

Lets create parent.js:

//parent.js
const cp = require('child_process');
let child = cp.fork('./child.js');

child.on('message', (message) =>{
 console.log('Parent got message: '+message);
 //this script got a message from child.js
});

child.send('Parent sent a message to child.js');
//send a message to child.js

Now create the child.js file:

//child.js

//got a message
process.on('message',(message)=>{
 console.log(message);
}

//send a message back to parent
process.send('A message from child.js');

Now execute the parent.js:

D:\>node parent
Parent got message: A message from child.js
Parent sent a message to child.js

In the next lesson, I will tell you how we will use child_process.fork() in our Duplicate File Finder application so that our application’s GUI is not blocked and we can easily pause and resume the application without blocking event loop.