Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Write the script as a Node.js program, then start commands with node:child_process. For most tasks, use spawn() or execFile() with a fixed executable and a separate argument array. Use exec() only when you deliberately need shell features such as pipes or redirection—and never build its command string from untrusted input.

What a JavaScript shell script is

JavaScript does not replace the operating system’s shell. A JavaScript shell script is a Node.js program that coordinates other programs by starting child processes. Node’s built-in node:child_process module provides the main APIs: spawn(), execFile(), and exec(). Their differences matter because they determine whether a shell parses the command, how output is delivered, and how much control you have over arguments.

Choose the right way to run a command

Option Best for Shell parsing Output Key consideration
spawn() Long-running tasks or output that should appear as it is produced Off by default Streams The executable and its flags may differ by operating system.
execFile() Running one executable with a bounded set of arguments Off by default on Unix-like systems Buffered result Windows .bat and .cmd files require a shell-aware strategy.
exec() Commands that need pipes, globs, redirection, or compound shell syntax On Buffered result with a configurable limit The command string is shell code; quoting and special characters vary by shell.
Google zx Concise shell-like automation using JavaScript control flow Uses a configurable shell wrapper Promise-based process result Still depends on the selected shell and installed commands.
ShellJS Scripts that want familiar Unix-like command APIs Library-dependent API-oriented Command behavior and availability can still vary across platforms.

These APIs and their options are documented by Node.js. The safest default is to keep the executable and flags in your code and pass variable values as individual arguments.

Run a command and stream its output with spawn()

Use spawn() when the process may run for a while or when you want its output to flow directly to the terminal. This example runs Git’s status command in the current working directory:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { spawn } from 'node:child_process';

const child = spawn('git', ['status', '--short'], { stdio: 'inherit' });
child.on('close', code => {
  if (code !== 0) process.exitCode = code ?? 1;
});

The executable, git, is separate from the argument array. That preserves argument boundaries and avoids asking a shell to interpret the command. Setting stdio: 'inherit' connects the child’s standard input, output, and error streams to the parent’s terminal. The close handler propagates a nonzero exit status so that a failed Git command does not look like a successful script run.

For production scripts, decide deliberately whether to set options such as cwd, env, signal, timeout, or killSignal. These control the child’s working directory, environment, and cancellation behavior; consult the Node.js child-process documentation for platform-specific details.

Capture a bounded result with execFile()

When you need the output of a single executable rather than live streaming, execFile() is a useful fit. Promisifying it lets you use await:

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const run = promisify(execFile);
const { stdout } = await run('node', ['--version']);
console.log(stdout.trim());

On Unix-like systems, execFile() does not spawn a shell by default. That avoids shell parsing for ordinary executable calls, but output is collected rather than streamed. On Windows, directly launching .bat or .cmd files needs special handling; follow Node’s documented Windows guidance instead of assuming the Unix-like behavior applies.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use exec() only when shell syntax is intentional

A pipe is shell grammar, so a shell must interpret it. For example, this command pipes Git output to head:

import { exec } from 'node:child_process';
import { promisify } from 'node:util';

const runShell = promisify(exec);
const { stdout } = await runShell('git status --short | head -n 20', {
  timeout: 10_000,
  maxBuffer: 1024 * 1024,
});
console.log(stdout);

Node passes the command string to a shell when you call exec(). Shell metacharacters, quoting rules, and even shell selection can change the meaning of that string. The Node.js documentation warns that untrusted input combined with shell execution can enable arbitrary command execution.

  • Do not concatenate user input, filenames, or other external values into an exec() command string.
  • Prefer spawn() or execFile() with an argument array when shell syntax is unnecessary.
  • If shell syntax is essential, keep the command structure fixed, validate any variable values, and set an appropriate timeout and output limit.

Use zx for concise shell-like automation

Google zx wraps child-process operations to make scripts read more like shell commands while retaining JavaScript features such as variables and await. Install it with npm install zx, save a script as an .mjs file, and run it using the zx CLI or its documented shebang:

#!/usr/bin/env zx

const branch = await $`git branch --show-current`;
await $`git checkout -b ${'feature/example'}`;
console.log(branch.stdout.trim());

The zx documentation says interpolated values are escaped and quoted by its template tag. You can select the shell through the API, CLI, or environment, so check which shell the script will use. Escaping does not remove the need to validate inputs or account for the commands and shell installed on the target machine. See the zx documentation for installation and usage details.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use ShellJS for Unix-like command ergonomics

ShellJS provides a Node.js API for familiar Unix shell commands and describes itself as portable across Windows, Linux, and macOS. It can make file operations and command-oriented scripts feel more familiar, but it does not make every underlying command identical across platforms. Review how the library invokes commands, and verify command availability and input handling for the environments where the script will run.

Make the script reliable across machines

A JavaScript wrapper may run on several operating systems while the command it launches does not. Portability depends on the executable, its flags, the shell (if any), path conventions, quoting rules, and Windows handling of .bat or .cmd files. Before relying on a script across platforms, make these choices explicit:

  • Input boundaries: Keep executable names and flags in code; pass external values as separate arguments and validate them.
  • Failure behavior: Check exit codes and make errors visible. A fulfilled promise alone is not proof that the external task succeeded.
  • Hanging processes: Add a timeout or cancellation signal for commands that may stall.
  • Output handling: Choose whether output should stream to the terminal, be captured in memory, or be written to a file. Set maxBuffer when using buffered APIs.
  • Reproducibility: Set the working directory and environment explicitly when the script depends on them.
  • Platform assumptions: Document the required shell, commands, paths, and Windows-specific behavior.

For API signatures, platform caveats, and process options, refer to the Node.js child_process reference. For zx’s shell configuration, consult its project documentation.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.