child_process.execSync()

Run a shell command, block until it finishes, and return its stdout.

Signature
child_process.execSync(command[, options])
  • Added in Node.js v0.11.12

Parameters

ParameterTypeDescription
$command<string>The command to run, interpreted by a shell.
$optionsoptional<Object>Execution options.
$options.cwdoptional<string> | <URL>= process.cwd()Current working directory of the child process.
$options.inputoptional<string> | <Buffer> | <TypedArray> | <DataView>Value passed as stdin to the spawned process. Supplying it overrides stdio[0].
$options.stdiooptional<string> | <Array>= 'pipe'Child's stdio configuration. The default pipes stdout back to you; 'inherit' sends the child's output straight to the terminal.
$options.envoptional<Object>= process.envEnvironment key-value pairs, replacing the child's environment entirely.
$options.shelloptional<string>= '/bin/sh' on Unix, process.env.ComSpec on WindowsShell to execute the command with.
$options.timeoutoptional<number>= undefinedMaximum milliseconds the process is allowed to run before killSignal is sent.
$options.killSignaloptional<string> | <integer>= 'SIGTERM'Signal used when the spawned process is killed.
$options.maxBufferoptional<number>= 1024 * 1024Largest amount of data in bytes allowed on stdout or stderr.
$options.encodingoptional<string>= 'buffer'Encoding used for all stdio inputs and outputs. Note this defaults to 'buffer', not 'utf8'.
$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>= falseHide the subprocess console window that would normally be created on Windows.

Return value

<Buffer> | <string>The stdout from the command. A Buffer by default, or a string when the encoding option is set.

Errors

Throws if the process exits non-zero, times out, or cannot be spawned. The thrown Error carries status, signal, stdout, stderr and output, and error.stderr is the only place the child's message can be read once stdio is piped.

Changelog

  • PHP v16.4.0, v14.18.0The cwd option can be a WHATWG URL object using the file: protocol.
  • PHP v10.10.0The input option can be any TypedArray or a DataView.
  • PHP v8.8.0The windowsHide option is supported.
  • PHP v8.0.0The input option can be a Uint8Array.

execSync() runs a command through a shell and blocks the entire Node process — event loop, timers, sockets and all — until it exits, then returns whatever the command wrote to stdout.

When to reach for it

Only in code where blocking is free: build scripts, CLI tools, npm lifecycle hooks, migrations, one-shot setup steps. Reading a git SHA at the top of a build script is the canonical use. Never call it inside a request handler or any long-lived server — one slow command freezes every concurrent connection.

What trips people up

  • The encoding default is 'buffer', not 'utf8'. Forgetting { encoding: 'utf8' } gives you a Buffer, and the almost-inevitable .trim() then fails.
  • Failure throws. A non-zero exit is an exception, so anything that can fail needs a try/catch; read err.status and err.stderr there.
  • With the default stdio: 'pipe' the child's stderr is captured, so error output vanishes unless you print err.stderr yourself. stdio: 'inherit' shows it live but then makes the return value null.
  • The command string is shell syntax, so interpolating a variable is a command injection hole. Use spawnSync() with an args array instead.

Example

Example · javascript
const { execSync } = require('node:child_process');

// Without encoding this would return a Buffer.
const out = execSync('echo hello', { encoding: 'utf8' });
console.log('stdout:', JSON.stringify(out));
console.log('type:', typeof out);

// A non-zero exit throws rather than returning a status.
try {
  execSync('echo boom 1>&2; exit 3', { encoding: 'utf8', stdio: 'pipe' });
} catch (err) {
  console.log('status:', err.status);
  console.log('stderr:', JSON.stringify(err.stderr));
}

// stdout: "hello\n"
// type: string
// status: 3
// stderr: "boom\n"

When to use it

  • Read the current git commit or branch at the start of a build script.
  • Run a database migration command in a deployment script and abort on failure.
  • Shell out to a packaging tool from a CLI where blocking is acceptable.

Frequently asked questions

How do I run a command synchronously in Node.js?

Call child_process.execSync(command, { encoding: 'utf8' }), which blocks until the command exits and returns its stdout as a string. Wrap it in try/catch, because a non-zero exit code throws instead of returning a status.

Why does execSync return a Buffer instead of a string?

Because the encoding option defaults to 'buffer', unlike exec() which defaults to 'utf8'. Pass { encoding: 'utf8' } to get a string back.

How do I see the error message when execSync fails?

Catch the thrown Error and read err.stderr, since the default stdio: 'pipe' captures the child's error output instead of printing it. The same Error also carries err.status for the exit code and err.stdout for anything printed before the failure.

More examples

Reading build metadata in a script

This is the pattern execSync() exists for: a build script that needs a value before it can continue, where blocking costs nothing. The output prints the shape rather than the values because a commit hash differs in every checkout. Setting stdio[0] to 'ignore' stops git from ever trying to read from the terminal, which is what causes a build to hang when a command unexpectedly prompts.

Example · javascript
const { execSync } = require('node:child_process');

function git(args) {
  return execSync(`git ${args}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
}

let meta;
try {
  meta = { commit: git('rev-parse --short HEAD'), branch: git('rev-parse --abbrev-ref HEAD') };
} catch (err) {
  // Not a git checkout, or git is not installed.
  console.log('git unavailable, status:', err.status);
  meta = { commit: 'unknown', branch: 'unknown' };
}

console.log('keys:', Object.keys(meta).sort());

// keys: [ 'branch', 'commit' ]

Streaming output to the terminal with stdio: inherit

With stdio: 'inherit' the child shares the parent's file descriptors, so its output appears live and in colour. The trade-off is visible in the last line: nothing was piped back, so the return value is null. Choose 'inherit' when a human is watching, and the default 'pipe' when your code needs the text.

Example · javascript
const { execSync } = require('node:child_process');

// 'inherit' lets the child write directly to this terminal, keeping colours
// and showing progress as it happens instead of after it finishes.
const result = execSync('echo step 1 && echo step 2', { stdio: 'inherit' });

console.log('return value:', result);

// step 1
// step 2
// return value: null

Feeding stdin with the input option

input writes the given string or Buffer to the child's stdin and then closes it, which is how you drive a filter command without a temporary file. Supplying input overrides whatever stdio[0] would have been, so the child never touches the parent's terminal.

Example · javascript
const { execSync } = require('node:child_process');

const payload = 'gamma\nalpha\nbeta\n';

const sorted = execSync('sort', {
  input: payload,
  encoding: 'utf8',
});

console.log(JSON.stringify(sorted));

// "alpha\nbeta\ngamma\n"

Discussion

  • Be the first to comment on this lesson.