Executing commands and files in NodeJS

There are a couple of ways to execute files and commands, but all use the module child_process.

  • child_process.exec creates a shell and also buffers the whole command’s generated output and sends it to a callback function. So use it only if the output is not too big
  • child_process.execFile runs a file without a shell but IO redirection and file clobbing are not supported. Also on Windows .bat or .cmd files cannot be executed without a shell
  • child_process.fork allows you to communicate between parent and child processes by sending messages
  • child_process.spawn recommended in most cases as it does not create a shell and works with streams, so it is a better choice when working with large generated command output

Streams in a parent process vs a child process

Unlike in a normal process, in a child process stdout and stderr are readable streams and stdin is writable.

in a parent processin a child process
stdin…is readable…is writable
stdout…is writable…is readable
stderr…is writable…is readable

We can read from readable streams using myStream.on('data', (data) => ...).

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

// we define our command that we want to call
const child = spawn('cmd', ['/c', 'dir']);

child.on('exit', (code, signal) => {
    console.log(`child process exited with code ${code} and signal ${signal}`);
});


/**
 * Unlike in a normal process, in a child process
 * stdout and stderr are readable streams and stdin is writable.
 */
child.stdout.on('data', (data) => {
    // this will print the directory listing
    console.log(`Child stdout: ${data}`);
});

child.stderr.on('data', (data) => {
    console.log(`Child stderr: ${data}`);
});

child.on('error', (err) => {
    console.log('Error occurred: ' + err);
})

Spawn with options

The following example is almost the same, but this time we inherit the stdio from the parent. Note, that we do not have to define child.stderr and so on explicitly:

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

const child = spawn('cmd', ['/c', 'dir'], {
  shell: true, // we can also use a shell like exec() would do
  stdio: 'inherit'
});

child.on('exit', (code, signal) => {
    console.log(`child process exited with code ${code} and signal ${signal}`);
});


/**
 * Unlike in a normal process, in a child process
 * stdout and stderr are readable streams and stdin is writable.
 */
child.on('data', (data) => {
    // this will print the directory listing
    console.log(`Child stdout: ${data}`);
});

child.on('data', (data) => {
    console.log(`Child stderr: ${data}`);
});

child.on('error', (err) => {
    console.log('Error occurred: ' + err);
})

Using env variables in processes

We can define our own environment variables (Unix):

const child = spawn('echo $ANSWER', {
  shell: true, // we can also use a shell like exec() would do
  stdio: 'inherit',
  env: { ANSWER: 42}
});

Detached mode for background processing

We can run child processes in detached mode, to let them run even if the parent process exited. This is useful for long running background processes:

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

const child = spawn('node', ['timer.js'], {
    detached: true,
    stdio: 'ignore' // must be set if detached mode enabled
});

// invoking unref() means that the parent process is allowed to exit
child.unref();

Parent forks a child process

Allows you to communicate between parent and child processes by sending messages.

Step 1: We create a (child-)script that we will later fork from the parent. It can receive messages from the parent and sends itself messages to the parent. Note that nothing in here says ‘fork’, we only use process.

process.on('message', (msg) => {
    console.log('Message from parent: ', msg);
});

let counter = 8;

setInterval(() => {
    process.send({counter: counter++});
}, 1000);

Step 2: We create the parent which forks the child script, sends one message to the child and receives messages from the child:

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

const forked = fork('child.js');

forked.on('message', (msg) => {
    console.log('Message from child: ', msg);
});

forked.send({hello: 'world'});

Console output will be:

Message from parent:  { hello: 'world' }
Message from child:  { counter: 8 }
Message from child:  { counter: 9 }
Message from child:  { counter: 10 }
Message from child:  { counter: 11 }
Message from child:  { counter: 12 }

About Author

Mathias Bothe To my job profile

I am Mathias, born 39 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 15 years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I am founder of bosy.com, creator of the security service platform BosyProtect┬ę and initiator of several other software projects.