child_process.execFile()
Run an executable directly, without a shell, and buffer its output into a callback.
child_process.execFile(file[, args][, options][, callback])- Added in Node.js v0.1.91
Parameters
| Parameter | Type | Description |
|---|---|---|
$file | <string> | The name or path of the executable file to run. Resolved against PATH when it is a bare name. |
$argsoptional | <string[]>= [] | List of string arguments. Each element is passed to the program verbatim, with no shell parsing. |
$optionsoptional | <Object> | Execution options. |
$options.cwdoptional | <string> | <URL>= process.cwd() | Current working directory of the child process. |
$options.envoptional | <Object>= process.env | Environment key-value pairs, replacing the child's environment entirely. |
$options.encodingoptional | <string>= 'utf8' | Encoding used to decode stdout and stderr. Use 'buffer' to receive Buffers. |
$options.timeoutoptional | <number>= 0 | Milliseconds after which the child is sent killSignal. 0 disables the timeout. |
$options.maxBufferoptional | <number>= 1024 * 1024 | Largest amount of data in bytes allowed on stdout or stderr before the child is killed. |
$options.killSignaloptional | <string> | <integer>= 'SIGTERM' | Signal used when the child is killed by timeout or maxBuffer overflow. |
$options.shelloptional | <boolean> | <string>= false | If true, runs the command inside a shell, which gives up the injection safety this function exists for. |
$options.signaloptional | <AbortSignal> | Allows aborting the child process using an AbortSignal. |
$options.uidoptional | <number> | Sets the user identity of the process (see setuid(2)). |
$options.gidoptional | <number> | Sets the group identity of the process (see setgid(2)). |
$options.windowsHideoptional | <boolean>= false | Hide the subprocess console window that would normally be created on Windows. |
$options.windowsVerbatimArgumentsoptional | <boolean>= false | No quoting or escaping of arguments is done on Windows. Ignored on Unix. |
$callbackoptional | <Function> | Called with (error, stdout, stderr) when the process terminates. |
Return value
<ChildProcess>A ChildProcess instance. The buffered output is delivered to the callback.
Errors
Does not throw for a failing command; a missing executable, a non-zero exit code, a timeout, or exceeding maxBuffer is reported as the error argument to the callback.
Changelog
- PHP v23.11.0, v22.15.0Passing args together with shell: true is deprecated.
- PHP v16.4.0, v14.18.0The cwd option can be a WHATWG URL object using the file: protocol.
- PHP v15.4.0, v14.17.0AbortSignal support was added via the signal option.
- PHP v8.8.0The windowsHide option is supported.
execFile() is exec() without the shell. The executable is launched directly and each entry in args reaches it as one argv element, so quoting, semicolons and $VAR are inert characters rather than syntax.
When to reach for it
This is the default choice for calling another program: it gives you buffered output like exec() while being safe to build from variables. Any time part of the command comes from a request, a config file, or a filename on disk, this is the function you want.
What trips people up
- No shell means no shell features. Pipes,
>redirects,&&and glob expansion do nothing;'*.txt'stays a literal asterisk. Do that work in JavaScript, or pipe two spawn() children together. - On Windows,
.cmdand.batfiles are not executables. Runningnpmoryarnneedsshell: true— and that reopens the injection risk. - The same 1 MiB
maxBufferceiling applies, and overflowing it kills the child. - Setting
shell: truehere makes it behave exactly likeexec(), and since v22.15.0 combining that withargsis deprecated.
Example
const { execFile } = require('node:child_process');
// A filename that would be dangerous in a shell string.
const suspicious = 'report.txt; echo INJECTED';
execFile('node', ['-e', 'console.log(process.argv[1])', suspicious], (error, stdout, stderr) => {
console.log('error:', error);
console.log('stdout:', JSON.stringify(stdout));
console.log('stderr:', JSON.stringify(stderr));
});
// error: null
// stdout: "report.txt; echo INJECTED\n"
// stderr: ""When to use it
- Run a converter or linter on a user-supplied filename without risking command injection.
- Call a system binary such as git or openssl with arguments built from variables.
- Read a short, bounded output from a CLI tool inside a request handler.
Frequently asked questions
What is the difference between execFile and exec in Node.js?
execFile() launches the executable directly with an array of arguments, while exec() passes a single string to a shell that parses it. execFile() is therefore immune to command injection but cannot use pipes, redirects or globs.
How do I run a command safely with user input in Node.js?
Use child_process.execFile(file, args) and put every user-supplied value in the args array, never in the command name. Each array element is delivered to the program as one argument, so shell metacharacters have no effect.
Why does execFile fail with ENOENT on Windows for npm?
npm, npx and yarn are .cmd batch wrappers rather than real executables, and execFile() can only launch executables. Run them with shell: true, or point file at the underlying node script directly.
More examples
Awaiting execFile and reading the exit code
The resolved value is { stdout, stderr }, exactly as with exec(). On a non-zero exit the promise rejects with an Error whose code is the exit status and which still carries whatever the child managed to print, so you can log the child's own error message alongside the status.
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const run = promisify(execFile);
const { stdout } = await run('node', ['-p', '6 * 7']);
console.log('answer:', stdout.trim());
try {
await run('node', ['-e', 'process.exit(3)']);
} catch (error) {
console.log('code:', error.code);
console.log('has stdout property:', 'stdout' in error);
}
// answer: 42
// code: 3
// has stdout property: trueA missing executable versus a failing one
Both paths deliver an Error to the same callback argument, but error.code means different things: a string like ENOENT when the process could never start, and a number when the process ran and chose that exit status. Branch on typeof error.code if you need to tell a broken install from a command that simply failed.
const { execFile } = require('node:child_process');
// The binary does not exist at all.
execFile('no-such-binary', ['--version'], (error) => {
console.log('spawn failure code:', error.code); // ENOENT
console.log('errno is a number:', typeof error.errno); // number
});
// The binary exists but exits non-zero.
execFile('node', ['-e', 'process.exit(2)'], (error) => {
console.log('exit failure code:', error.code); // 2
console.log('killed:', error.killed); // false
});Enforcing a timeout on an untrusted tool
timeout plus killSignal bounds how long a third-party binary may run, and killed: true distinguishes a kill from an ordinary non-zero exit. Lowering maxBuffer at the same time bounds memory, since the default 1 MiB is per stream and is held in the parent process.
const { execFile } = require('node:child_process');
execFile(
'node',
['-e', 'setInterval(() => {}, 1000)'],
{ timeout: 500, killSignal: 'SIGKILL', maxBuffer: 64 * 1024 },
(error, stdout) => {
console.log('killed:', error.killed);
console.log('signal:', error.signal);
console.log('stdout so far:', JSON.stringify(stdout));
},
);
// killed: true
// signal: SIGKILL
// stdout so far: ""
Discussion