NodeJS: Executing commands and files

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

  • 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 rediretion 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 does not create a shell and works with streams, so it is a better choice when working with large generated command output.

It is recommended to use spawn in most cases.

processchild
stdinreadablewritable
stdoutwritablereadable
stderrwritablereadable
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);
})

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);
})

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}
});

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();

Forked 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 Contact me

I am Mathias, born 38 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 14 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.