Child Processes
Spawn OS processes and shell commands from Node.js using exec, spawn, and fork
Overview
Key Points
- β’exec() buffers full output as a string β use for short commands; risky for large output
- β’spawn() streams stdout/stderr β use for long-running processes or large output
- β’execFile() bypasses the shell, making it safer and faster than exec() when you control the arguments
- β’fork() creates a Node.js child with an IPC channel for message passing β ideal for CPU-intensive JS work
- β’Child process stdin/stdout/stderr are separate from the parent unless you use { stdio: "inherit" }
- β’Always handle the "error" and "close" events on child processes to detect failures
Examples
exec() β run a shell command and get the output as a string
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
async function getGitLog() {
try {
const { stdout, stderr } = await execAsync('git log --oneline -5');
console.log('Last 5 commits:\n', stdout);
if (stderr) console.warn('Stderr:', stderr);
} catch (err) {
console.error('Git error:', err.message);
}
}
getGitLog();exec() uses the shell, so you can use pipes: exec("cat file.txt | grep error"). But this also means you must sanitize any user input to prevent shell injection.
spawn() β stream output from a long-running process
const { spawn } = require('child_process');
function runNpmInstall() {
const child = spawn('npm', ['install'], {
cwd: process.cwd(),
stdio: ['ignore', 'pipe', 'pipe'], // stdin ignored, stdout/stderr piped
});
child.stdout.on('data', (chunk) => {
process.stdout.write(chunk); // stream to parent stdout in real time
});
child.stderr.on('data', (chunk) => {
process.stderr.write(chunk);
});
child.on('close', (code) => {
console.log(`npm install exited with code ${code}`);
});
child.on('error', (err) => {
console.error('Failed to start child process:', err);
});
}
runNpmInstall();fork() β Node.js to Node.js IPC for CPU-heavy work
// parent.js
const { fork } = require('child_process');
const path = require('path');
const child = fork(path.join(__dirname, 'worker.js'));
child.send({ start: 1, end: 100_000_000 }); // ask child to compute sum
child.on('message', (result) => {
console.log('Sum received from child:', result.sum);
child.kill();
});
// worker.js
process.on('message', ({ start, end }) => {
let sum = 0;
for (let i = start; i <= end; i++) sum += i;
process.send({ sum }); // send result back
});fork() is the only child_process method that sets up a two-way message channel. The child must be a Node.js script.
execFile() β safer than exec for calling known executables
const { execFile } = require('child_process');
const { promisify } = require('util');
const execFileAsync = promisify(execFile);
async function listFiles(directory) {
// execFile does NOT use a shell β args are passed as an array
// This prevents shell injection if directory comes from user input
const { stdout } = await execFileAsync('ls', ['-la', directory]);
console.log(stdout);
}
// Safe even if directory contains spaces or special characters
listFiles('/home/alice/my documents');Never pass unsanitized user input to exec(). Use execFile() with argument arrays instead.
Common Mistakes
- Using exec() with user-supplied input β this is a shell injection vulnerability; use execFile() with argument arrays instead
- Not handling the "error" event β if the executable is not found, an "error" fires (not "close" with a non-zero code)
- Using exec() for commands that produce large output β stdout is buffered; large output causes "maxBuffer exceeded" errors
- Forgetting that child processes have their own memory space β you cannot share JavaScript objects directly, only serialize them via IPC or files
Note
Promisify exec and execFile with util.promisify() for clean async/await usage. For spawn(), wrap it in a Promise manually since it returns a stream-based API that promisify does not handle well.
Exercises
Write Node.js code in your local terminal. Use the editor below to follow along, then mark each exercise as done once it works.
Completed: 0 / 6 exercises
Knowledge Check
Question 1 of 4