← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / child_process

child_process

Running shell commands and spawning processes from Node.js — exec, spawn, execFile, fork, and handling output.

What it is, in plain English: The child_process module lets Node.js spawn external processes. exec() runs a shell command and buffers the output — good for short commands. spawn() runs a command with streaming output — good for long-running processes. execFile() runs an executable directly (no shell — safer and faster). fork() spawns a new Node.js process and sets up message-passing between parent and child.
Where this bites you

exec() collects a command's entire output in memory and gives up past maxBuffer, which is 1 MB by default. A command that prints more than that, such as a long git log, a database dump or a verbose build, is killed and the call fails with ERR_CHILD_PROCESS_STDIO_MAXBUFFER. It works in testing with small output, then fails in production with real data.

import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);

// a command with a lot of output -- here 2 MB, like a big `git log` or a database dump
try {
  const { stdout } = await execAsync(`node -e "process.stdout.write('x'.repeat(2_000_000))"`);
  console.log("got", stdout.length, "bytes");
} catch (e) {
  console.log(e.code, "|", e.message);
}
// ERR_CHILD_PROCESS_STDIO_MAXBUFFER | stdout maxBuffer length exceeded

Running commands from Node.js

exec — run a shell command

import { exec }   from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);

// Run a git command:
const { stdout, stderr } = await execAsync("git log --oneline -5");
console.log(stdout);  // last 5 commits

// Check Node.js version:
const { stdout: version } = await execAsync("node --version");
console.log(version.trim());  // "v20.15.0"

// Handle errors:
try {
  await execAsync("ls /nonexistent");
} catch (e) {
  console.error("Command failed:", e.message);
  console.error("Exit code:", e.code);     // e.g. 2
  console.error("Stderr:", e.stderr);
}

Use exec() for commands with small, predictable output, like the version check above. For anything that can grow, use spawn() (next section), which streams the output in chunks with no size limit. If you really need it all as one string, you can raise the limit with exec(cmd, { maxBuffer: 50 * 1024 * 1024 }), but that just moves the cliff.

import { spawn } from "child_process";

// spawn streams the output instead of collecting it into one buffer:
const child = spawn("node", ["-e", "process.stdout.write('x'.repeat(2_000_000))"]);
let bytes = 0;
child.stdout.on("data", (chunk) => { bytes += chunk.length; });   // process as it arrives
child.on("close", (code) => console.log("exit", code, "| got", bytes, "bytes"));
// exit 0 | got 2000000 bytes

spawn — streaming output for long commands

import { spawn } from "child_process";

// Good for: npm install, build tools, long-running processes
const install = spawn("npm", ["install"], {
  cwd:   "./my-project",  // working directory
  stdio: "pipe",          // capture stdout/stderr (default)
});

install.stdout.on("data", (chunk) => process.stdout.write(chunk));
install.stderr.on("data", (chunk) => process.stderr.write(chunk));

install.on("close", (code) => {
  if (code === 0) console.log("Install successful!");
  else            console.error(`Install failed with code ${code}`);
});

// Kill a process:
const proc = spawn("long-running-command");
setTimeout(() => proc.kill("SIGTERM"), 10000);  // kill after 10s
Shell injection risk. Never pass user input directly to exec() — it goes through the shell. Use execFile() or spawn() with an args array instead.
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.