Home

spawn and spawnSync

This is meant as a follow-up to the exec post.

Some differences between spawn and exec

  • spawn takes an array of input strings where exec takes a single string
  • spawn returns an object with a bunch of keys: 'status', 'signal', 'output', 'pid', 'stdout', 'stderr' where as exec returns 3 items: err, stdout, stderr

a synchronous approach with spawnSync

// the dependency
const { spawnSync } = require('child_process');

// a process to run - here, node evaluating a console.log
const COMMAND_TO_RUN = `node -e "console.log('string from node -e nested string')"`;

// running the command
const execOut = spawnSync(COMMAND_TO_RUN);

// doing something with the output
console.log(execOut)

The output of the above looks something like...

{
  status: 0,
  signal: null,
  output: [
    null,
    <Buffer 73 74 72 69 6e 67 20 66 72 6f 6d 20 6e 6f 64 65 20 2d 65 20 6e 65 73 74 65 64 20 73 74 72 69 6e 67 0a>,
    <Buffer >
  ],
  pid: 14049,
  stdout: <Buffer 73 74 72 69 6e 67 20 66 72 6f 6d 20 6e 6f 64 65 20 2d 65 20 6e 65 73 74 65 64 20 73 74 72 69 6e 67 0a>,
  stderr: <Buffer >
}

To get the stdout value as a string the example can be updated to use the stdout value of the output object:

// the dependency
const { spawnSync } = require('child_process');

// a process to run - here, node evaluating a console.log
const COMMAND_TO_RUN = `node -e "console.log('string from node -e nested string')"`;

// running the command
const spawnOut = spawnSync(COMMAND_TO_RUN);

// doing something with the output
console.log(spawnOut.stdout.toString())

Here's an example of runing some math:

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

const COMMAND_TO_RUN = ['-p', "4 + 7"];

const spawnOut = spawnSync(`${process.execPath}`, COMMAND_TO_RUN);
console.log(Number(spawnOut.stdout.toString()));

passing input to the process

For a more verbose set of examples on custom configuration see this other post on the topic.
Here, the child processes input is sent as key/value pair of the spawnSync optional config object.
The child processes stdin, stdout, and stderr are also configured in the optional config object.

const { spawnSync } = require('child_process');
const COMMAND_TO_RUN = `console.error('err output'); process.stdin.pipe(process.stdout)`;
const INPUT_FOR_PROCESS = 'an input string from the parent\n';
spawnSync(process.execPath, ['-e', COMMAND_TO_RUN], {
  input: INPUT_FOR_PROCESS,
  stdio: ['pipe', 'inherit', process.stdout],
});

respond to errors thrown from the child_process

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

const COMMAND_TO_RUN = ['-e', "throw new Error('this is an error')"];

const spawnOut = spawnSync(`${process.execPath}`, COMMAND_TO_RUN);

// EITHER of these will show the error
// console.log(spawnOut.stderr.toString());
// console.log(spawnOut.output[2].toString());

an async approach with spawn

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

const COMMAND_TO_RUN = ['-p', "4 + 7"];

const spawnObj = spawn(`${process.execPath}`, COMMAND_TO_RUN);
console.log(`parent pid: ${process.pid}`)
console.log(`spawned pid: `, spawnObj.pid);

spawnObj.stdout.pipe(process.stdout)
spawnObj.on('close', (exitStatusCode) => {
  console.log(`spawned process exit status: ${exitStatusCode}`);
});

the spawn api is a bit different than the spawnSync api in that the spawnSync approach only returns the result of the spawned process upon completion of the process. The spawn method also does not stop the buffering of the child process like the other methods of child-process creation. spawn will stream child-process output regardless of the size of the child-process output size, whereas the other methods of child-process creation all have maxBuffer settings and configurations.

Tags: