We can create a child process in four different ways:
- spawn
- fork
- exec
- 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 usefork
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 spawn
the 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 processon('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.