Asynchronous process creation
History
The child_process.spawn(), child_process.fork(), child_process.exec(),
and child_process.execFile() methods all follow the idiomatic asynchronous
programming pattern typical of other Node.js APIs.
Each of the methods returns a ChildProcess instance. These objects
implement the Node.js EventEmitter API, allowing the parent process to
register listener functions that are called when certain events occur during
the life cycle of the child process.
The child_process.exec() and child_process.execFile() methods
additionally allow for an optional callback function to be specified that is
invoked when the child process terminates.
The importance of the distinction between child_process.exec() and
child_process.execFile() can vary based on platform. On Unix-type
operating systems (Unix, Linux, macOS) child_process.execFile() can be
more efficient because it does not spawn a shell by default. On Windows,
however, .bat and .cmd files are not executable on their own without a
terminal, and therefore cannot be launched using child_process.execFile().
When running on Windows, .bat and .cmd files can be invoked by:
- using
child_process.spawn()with theshelloption set (not recommended, see DEP0190), or - using
child_process.exec(), or - spawning
cmd.exeand passing the.bator.cmdfile as an argument (which is whatchild_process.exec()does internally).
In any case, if the script filename contains spaces, it needs to be quoted.
const { exec, spawn } = require('node:child_process'); exec('my.bat', (err, stdout, stderr) => { /* ... */ }); // Or, spawning cmd.exe directly: const bat = spawn('cmd.exe', ['/c', 'my.bat']); // If the script filename contains spaces, it needs to be quoted exec('"my script.cmd" a b', (err, stdout, stderr) => { /* ... */ });
import { exec, spawn } from 'node:child_process'; exec('my.bat', (err, stdout, stderr) => { /* ... */ }); // Or, spawning cmd.exe directly: const bat = spawn('cmd.exe', ['/c', 'my.bat']); // If the script filename contains spaces, it needs to be quoted exec('"my script.cmd" a b', (err, stdout, stderr) => { /* ... */ });
child_process.exec(command, options?, callback?): ChildProcess
stringObjectObjectprocess.env.string'utf8'string'/bin/sh' on Unix, process.env.ComSpec on Windows.AbortSignalnumber0numbermaxBuffer and Unicode.
Default: 1024 * 1024.booleanfalse.FunctionChildProcessSpawns a shell then executes the command within that shell, buffering any
generated output. The command string passed to the exec function is processed
directly by the shell and special characters (vary based on
shell)
need to be dealt with accordingly:
const { exec } = require('node:child_process'); exec('"/path/to/test file/test.sh" arg1 arg2'); // Double quotes are used so that the space in the path is not interpreted as // a delimiter of multiple arguments. exec('echo "The \\$HOME variable is $HOME"'); // The $HOME variable is escaped in the first instance, but not in the second.
import { exec } from 'node:child_process'; exec('"/path/to/test file/test.sh" arg1 arg2'); // Double quotes are used so that the space in the path is not interpreted as // a delimiter of multiple arguments. exec('echo "The \\$HOME variable is $HOME"'); // The $HOME variable is escaped in the first instance, but not in the second.
Never pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution.
If a callback function is provided, it is called with the arguments
(error, stdout, stderr). On success, error will be null. On error,
error will be an instance of Error. The error.code property will be
the exit code of the process. By convention, any exit code other than 0
indicates an error. error.signal will be the signal that terminated the
process.
The stdout and stderr arguments passed to the callback will contain the
stdout and stderr output of the child process. By default, Node.js will decode
the output as UTF-8 and pass strings to the callback. The encoding option
can be used to specify the character encoding used to decode the stdout and
stderr output. If encoding is 'buffer', or an unrecognized character
encoding, Buffer objects will be passed to the callback instead.
const { exec } = require('node:child_process'); exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } console.log(`stdout: ${stdout}`); console.error(`stderr: ${stderr}`); });
import { exec } from 'node:child_process'; exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } console.log(`stdout: ${stdout}`); console.error(`stderr: ${stderr}`); });
If timeout is greater than 0, the parent process will send the signal
identified by the killSignal property (the default is 'SIGTERM') if the
child process runs longer than timeout milliseconds.
Unlike the exec(3) POSIX system call, child_process.exec() does not replace
the existing process and uses a shell to execute the command.
If this method is invoked as its util.promisify()ed version, it returns
a Promise for an Object with stdout and stderr properties. The returned
ChildProcess instance is attached to the Promise as a child property. In
case of an error (including any error resulting in an exit code other than 0), a
rejected promise is returned, with the same error object given in the
callback, but with two additional properties stdout and stderr.
const util = require('node:util'); const exec = util.promisify(require('node:child_process').exec); async function lsExample() { const { stdout, stderr } = await exec('ls'); console.log('stdout:', stdout); console.error('stderr:', stderr); } lsExample();
import { promisify } from 'node:util'; import child_process from 'node:child_process'; const exec = promisify(child_process.exec); async function lsExample() { const { stdout, stderr } = await exec('ls'); console.log('stdout:', stdout); console.error('stderr:', stderr); } lsExample();
If the signal option is enabled, calling .abort() on the corresponding
AbortController is similar to calling .kill() on the child process except
the error passed to the callback will be an AbortError:
const { exec } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = exec('grep ssh', { signal }, (error) => { console.error(error); // an AbortError }); controller.abort();
import { exec } from 'node:child_process'; const controller = new AbortController(); const { signal } = controller; const child = exec('grep ssh', { signal }, (error) => { console.error(error); // an AbortError }); controller.abort();
child_process.execFile(file, args?, options?, callback?): ChildProcess
stringstring[]ObjectObjectprocess.env.string'utf8'number0numbermaxBuffer and Unicode.
Default: 1024 * 1024.booleanfalse.booleanfalse.true, runs command inside of a shell. Uses
'/bin/sh' on Unix, and process.env.ComSpec on Windows. A different
shell can be specified as a string. See Shell requirements and
Default Windows shell. Default: false (no shell).AbortSignalFunctionChildProcessThe child_process.execFile() function is similar to child_process.exec()
except that it does not spawn a shell by default. Rather, the specified
executable file is spawned directly as a new process making it slightly more
efficient than child_process.exec().
The same options as child_process.exec() are supported. Since a shell is
not spawned, behaviors such as I/O redirection and file globbing are not
supported.
const { execFile } = require('node:child_process'); const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; } console.log(stdout); });
import { execFile } from 'node:child_process'; const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; } console.log(stdout); });
The stdout and stderr arguments passed to the callback will contain the
stdout and stderr output of the child process. By default, Node.js will decode
the output as UTF-8 and pass strings to the callback. The encoding option
can be used to specify the character encoding used to decode the stdout and
stderr output. If encoding is 'buffer', or an unrecognized character
encoding, Buffer objects will be passed to the callback instead.
If this method is invoked as its util.promisify()ed version, it returns
a Promise for an Object with stdout and stderr properties. The returned
ChildProcess instance is attached to the Promise as a child property. In
case of an error (including any error resulting in an exit code other than 0), a
rejected promise is returned, with the same error object given in the
callback, but with two additional properties stdout and stderr.
const util = require('node:util'); const execFile = util.promisify(require('node:child_process').execFile); async function getVersion() { const { stdout } = await execFile('node', ['--version']); console.log(stdout); } getVersion();
import { promisify } from 'node:util'; import child_process from 'node:child_process'; const execFile = promisify(child_process.execFile); async function getVersion() { const { stdout } = await execFile('node', ['--version']); console.log(stdout); } getVersion();
If the shell option is enabled, do not pass unsanitized user input to this
function. Any input containing shell metacharacters may be used to trigger
arbitrary command execution.
If the signal option is enabled, calling .abort() on the corresponding
AbortController is similar to calling .kill() on the child process except
the error passed to the callback will be an AbortError:
const { execFile } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = execFile('node', ['--version'], { signal }, (error) => { console.error(error); // an AbortError }); controller.abort();
import { execFile } from 'node:child_process'; const controller = new AbortController(); const { signal } = controller; const child = execFile('node', ['--version'], { signal }, (error) => { console.error(error); // an AbortError }); controller.abort();
child_process.fork
History
modulePath parameter can be a WHATWG URL object using file: protocol.cwd option can be a WHATWG URL object using file: protocol.serialization option is supported now.stdio option can now be a string.stdio option is supported now.child_process.fork(modulePath, args?, options?): ChildProcess
string[]Objectbooleanoptions.detached).Objectprocess.env.stringstring[]process.execArgv.string'json' and 'advanced'.
See Advanced serialization for more details. Default: 'json'.AbortSignal'SIGTERM'.booleantrue, stdin, stdout, and stderr of the child
process will be piped to the parent process, otherwise they will be inherited
from the parent process, see the 'pipe' and 'inherit' options for
child_process.spawn()'s stdio for more details.
Default: false.child_process.spawn()'s stdio.
When this option is provided, it overrides silent. If the array variant
is used, it must contain exactly one item with value 'ipc' or an error
will be thrown. For instance [0, 1, 2, 'ipc'].booleanfalse.numberundefined.ChildProcessThe child_process.fork() method is a special case of
child_process.spawn() used specifically to spawn new Node.js processes.
Like child_process.spawn(), a ChildProcess object is returned. The
returned ChildProcess will have an additional communication channel
built-in that allows messages to be passed back and forth between the parent and
child. See subprocess.send() for details.
Keep in mind that spawned Node.js child processes are independent of the parent with exception of the IPC communication channel that is established between the two. Each process has its own memory, with their own V8 instances. Because of the additional resource allocations required, spawning a large number of child Node.js processes is not recommended.
By default, child_process.fork() will spawn new Node.js instances using the
process.execPath of the parent process. The execPath property in the
options object allows for an alternative execution path to be used.
Node.js processes launched with a custom execPath will communicate with the
parent process using the file descriptor (fd) identified using the
environment variable NODE_CHANNEL_FD on the child process.
Unlike the fork(2) POSIX system call, child_process.fork() does not clone the
current process.
The shell option available in child_process.spawn() is not supported by
child_process.fork() and will be ignored if set.
If the signal option is enabled, calling .abort() on the corresponding
AbortController is similar to calling .kill() on the child process except
the error passed to the callback will be an AbortError:
const { fork } = require('node:child_process'); if (process.argv[2] === 'child') { setTimeout(() => { console.log(`Hello from ${process.argv[2]}!`); }, 1_000); } else { const controller = new AbortController(); const { signal } = controller; const child = fork(__filename, ['child'], { signal }); child.on('error', (err) => { // This will be called with err being an AbortError if the controller aborts }); controller.abort(); // Stops the child process }
import { fork } from 'node:child_process'; import process from 'node:process'; if (process.argv[2] === 'child') { setTimeout(() => { console.log(`Hello from ${process.argv[2]}!`); }, 1_000); } else { const controller = new AbortController(); const { signal } = controller; const child = fork(import.meta.url, ['child'], { signal }); child.on('error', (err) => { // This will be called with err being an AbortError if the controller aborts }); controller.abort(); // Stops the child process }
child_process.spawn
History
args when shell is set to true is deprecated.cwd option can be a WHATWG URL object using file: protocol.serialization option is supported now.windowsHide option is supported now.argv0 option is supported now.shell option is supported now.child_process.spawn(command, args?, options?): ChildProcess
stringstring[]ObjectObjectprocess.env.stringargv[0] sent to the child
process. This will be set to command if not specified.options.stdio).booleanoptions.detached).string'json' and 'advanced'.
See Advanced serialization for more details. Default: 'json'.true, runs command inside of a shell. Uses
'/bin/sh' on Unix, and process.env.ComSpec on Windows. A different
shell can be specified as a string. See Shell requirements and
Default Windows shell. Default: false (no shell).booleantrue automatically
when shell is specified and is CMD. Default: false.booleanfalse.AbortSignalnumberundefined.ChildProcessThe child_process.spawn() method spawns a new process using the given
command, with command-line arguments in args. If omitted, args defaults
to an empty array.
If the shell option is enabled, do not pass unsanitized user input to this
function. Any input containing shell metacharacters may be used to trigger
arbitrary command execution.
A third argument may be used to specify additional options, with these defaults:
const defaults = { cwd: undefined, env: process.env, };
Use cwd to specify the working directory from which the process is spawned.
If not given, the default is to inherit the current working directory. If given,
but the path does not exist, the child process emits an ENOENT error
and exits immediately. ENOENT is also emitted when the command
does not exist.
Use env to specify environment variables that will be visible to the new
process, the default is process.env.
undefined values in env will be ignored.
Example of running ls -lh /usr, capturing stdout, stderr, and the
exit code:
const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code ${code}`); });
import { spawn } from 'node:child_process'; import { once } from 'node:events'; const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); const [code] = await once(ls, 'close'); console.log(`child process exited with code ${code}`);
Example: A very elaborate way to run ps ax | grep ssh
const { spawn } = require('node:child_process'); const ps = spawn('ps', ['ax']); const grep = spawn('grep', ['ssh']); ps.stdout.on('data', (data) => { grep.stdin.write(data); }); ps.stderr.on('data', (data) => { console.error(`ps stderr: ${data}`); }); ps.on('close', (code) => { if (code !== 0) { console.log(`ps process exited with code ${code}`); } grep.stdin.end(); }); grep.stdout.on('data', (data) => { console.log(data.toString()); }); grep.stderr.on('data', (data) => { console.error(`grep stderr: ${data}`); }); grep.on('close', (code) => { if (code !== 0) { console.log(`grep process exited with code ${code}`); } });
import { spawn } from 'node:child_process'; const ps = spawn('ps', ['ax']); const grep = spawn('grep', ['ssh']); ps.stdout.on('data', (data) => { grep.stdin.write(data); }); ps.stderr.on('data', (data) => { console.error(`ps stderr: ${data}`); }); ps.on('close', (code) => { if (code !== 0) { console.log(`ps process exited with code ${code}`); } grep.stdin.end(); }); grep.stdout.on('data', (data) => { console.log(data.toString()); }); grep.stderr.on('data', (data) => { console.error(`grep stderr: ${data}`); }); grep.on('close', (code) => { if (code !== 0) { console.log(`grep process exited with code ${code}`); } });
Example of checking for failed spawn:
const { spawn } = require('node:child_process'); const subprocess = spawn('bad_command'); subprocess.on('error', (err) => { console.error('Failed to start subprocess.'); });
import { spawn } from 'node:child_process'; const subprocess = spawn('bad_command'); subprocess.on('error', (err) => { console.error('Failed to start subprocess.'); });
Certain platforms (macOS, Linux) will use the value of argv[0] for the process
title while others (Windows, SunOS) will use command.
Node.js overwrites argv[0] with process.execPath on startup, so
process.argv[0] in a Node.js child process will not match the argv0
parameter passed to spawn from the parent. Retrieve it with the
process.argv0 property instead.
If the signal option is enabled, calling .abort() on the corresponding
AbortController is similar to calling .kill() on the child process except
the error passed to the callback will be an AbortError:
const { spawn } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const grep = spawn('grep', ['ssh'], { signal }); grep.on('error', (err) => { // This will be called with err being an AbortError if the controller aborts }); controller.abort(); // Stops the child process
import { spawn } from 'node:child_process'; const controller = new AbortController(); const { signal } = controller; const grep = spawn('grep', ['ssh'], { signal }); grep.on('error', (err) => { // This will be called with err being an AbortError if the controller aborts }); controller.abort(); // Stops the child process
On Windows, setting options.detached to true makes it possible for the
child process to continue running after the parent exits. The child process
will have its own console window. Once enabled for a child process,
it cannot be disabled.
On non-Windows platforms, if options.detached is set to true, the child
process will be made the leader of a new process group and session. Child
processes may continue running after the parent exits regardless of whether
they are detached or not. See setsid(2) for more information.
By default, the parent will wait for the detached child process to exit.
To prevent the parent process from waiting for a given subprocess to exit, use
the subprocess.unref() method. Doing so will cause the parent process' event
loop to not include the child process in its reference count, allowing the
parent process to exit independently of the child process, unless there is an established
IPC channel between the child and the parent processes.
When using the detached option to start a long-running process, the process
will not stay running in the background after the parent exits unless it is
provided with a stdio configuration that is not connected to the parent.
If the parent process' stdio is inherited, the child process will remain attached
to the controlling terminal.
Example of a long-running process, by detaching and also ignoring its parent
stdio file descriptors, in order to ignore the parent's termination:
const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, stdio: 'ignore', }); subprocess.unref();
import { spawn } from 'node:child_process'; import process from 'node:process'; const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, stdio: 'ignore', }); subprocess.unref();
Alternatively one can redirect the child process' output into files:
const { openSync } = require('node:fs'); const { spawn } = require('node:child_process'); const out = openSync('./out.log', 'a'); const err = openSync('./out.log', 'a'); const subprocess = spawn('prg', [], { detached: true, stdio: [ 'ignore', out, err ], }); subprocess.unref();
import { openSync } from 'node:fs'; import { spawn } from 'node:child_process'; const out = openSync('./out.log', 'a'); const err = openSync('./out.log', 'a'); const subprocess = spawn('prg', [], { detached: true, stdio: [ 'ignore', out, err ], }); subprocess.unref();
The options.stdio option is used to configure the pipes that are established
between the parent and child process. By default, the child's stdin, stdout,
and stderr are redirected to corresponding subprocess.stdin,
subprocess.stdout, and subprocess.stderr streams on the
ChildProcess object. This is equivalent to setting the options.stdio
equal to ['pipe', 'pipe', 'pipe'].
For convenience, options.stdio may be one of the following strings:
'pipe': equivalent to['pipe', 'pipe', 'pipe'](the default)'overlapped': equivalent to['overlapped', 'overlapped', 'overlapped']'ignore': equivalent to['ignore', 'ignore', 'ignore']'inherit': equivalent to['inherit', 'inherit', 'inherit']or[0, 1, 2]
Otherwise, the value of options.stdio is an array where each index corresponds
to an fd in the child. The fds 0, 1, and 2 correspond to stdin, stdout,
and stderr, respectively. Additional fds can be specified to create additional
pipes between the parent and child. The value is one of the following:
-
'pipe': Create a pipe between the child process and the parent process. The parent end of the pipe is exposed to the parent as a property on thechild_processobject assubprocess.stdio[fd]. Pipes created for fds 0, 1, and 2 are also available assubprocess.stdin,subprocess.stdoutandsubprocess.stderr, respectively. These are not actual Unix pipes and therefore the child process can not use them by their descriptor files, e.g./dev/fd/2or/dev/stdout. -
'overlapped': Same as'pipe'except that theFILE_FLAG_OVERLAPPEDflag is set on the handle. This is necessary for overlapped I/O on the child process's stdio handles. See the docs for more details. This is exactly the same as'pipe'on non-Windows systems. -
'ipc': Create an IPC channel for passing messages/file descriptors between parent and child. AChildProcessmay have at most one IPC stdio file descriptor. Setting this option enables thesubprocess.send()method. If the child process is a Node.js instance, the presence of an IPC channel will enableprocess.send()andprocess.disconnect()methods, as well as'disconnect'and'message'events within the child process.Accessing the IPC channel fd in any way other than
process.send()or using the IPC channel with a child process that is not a Node.js instance is not supported. -
'ignore': Instructs Node.js to ignore the fd in the child. While Node.js will always open fds 0, 1, and 2 for the processes it spawns, setting the fd to'ignore'will cause Node.js to open/dev/nulland attach it to the child's fd. -
'inherit': Pass through the corresponding stdio stream to/from the parent process. In the first three positions, this is equivalent toprocess.stdin,process.stdout, andprocess.stderr, respectively. In any other position, equivalent to'ignore'. -
Streamobject: Share a readable or writable stream that refers to a tty, file, socket, or a pipe with the child process. The stream's underlying file descriptor is duplicated in the child process to the fd that corresponds to the index in thestdioarray. The stream must have an underlying descriptor (file streams do not start until the'open'event has occurred). NOTE: While it is technically possible to passstdinas a writable orstdout/stderras readable, it is not recommended. Readable and writable streams are designed with distinct behaviors, and using them incorrectly (e.g., passing a readable stream where a writable stream is expected) can lead to unexpected results or errors. This practice is discouraged as it may result in undefined behavior or dropped callbacks if the stream encounters errors. Always ensure thatstdinis used as readable andstdout/stderras writable to maintain the intended flow of data between the parent and child processes. -
Positive integer: The integer value is interpreted as a file descriptor that is open in the parent process. It is shared with the child process, similar to how
Streamobjects can be shared. Passing sockets is not supported on Windows. -
null,undefined: Use default value. For stdio fds 0, 1, and 2 (in other words, stdin, stdout, and stderr) a pipe is created. For fd 3 and up, the default is'ignore'.
const { spawn } = require('node:child_process'); // Child will use parent's stdios. spawn('prg', [], { stdio: 'inherit' }); // Spawn child sharing only stderr. spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] }); // Open an extra fd=4, to interact with programs presenting a // startd-style interface. spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
import { spawn } from 'node:child_process'; import process from 'node:process'; // Child will use parent's stdios. spawn('prg', [], { stdio: 'inherit' }); // Spawn child sharing only stderr. spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] }); // Open an extra fd=4, to interact with programs presenting a // startd-style interface. spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
It is worth noting that when an IPC channel is established between the
parent and child processes, and the child process is a Node.js instance,
the child process is launched with the IPC channel unreferenced (using
unref()) until the child process registers an event handler for the
'disconnect' event or the 'message' event. This allows the
child process to exit normally without the process being held open by the
open IPC channel.
See also: child_process.exec() and child_process.fork().