Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Running Processes

Running child processes with proc is designed to be as simple and intuitive as possible, eliminating the boilerplate typically required for process management.

Getting Started

The most basic usage requires just importing run and calling it with your command:

import { run } from "jsr:@j50n/proc@0.24.6";

// Run a command
await run("ls", "-la").lines.collect();

That’s all you need. No configuration objects, no callback functions, no complex setup—just run the command and get the results.

Understanding Command Arguments

The first parameter to run() is the command name, and all subsequent parameters are individual arguments. This approach prevents shell injection vulnerabilities and makes argument handling explicit:

run("command", "arg1", "arg2", "arg3");

It’s important to pass arguments as separate parameters rather than as a single string:

// ✅ Correct
run("ls", "-la", "/home");

// ❌ Wrong - this won't work
run("ls -la /home");

Capturing Output

As an Array

const lines = await run("ls", "-la").lines.collect();
// lines is string[]

Line by Line

for await (const line of run("ls", "-la").lines) {
  console.log(line);
}

First or Last Line

const first = await run("ls").lines.first;
const last = await run("ls").lines.last;

As Raw Bytes

const bytes = await run("cat", "file.bin").collect();
// bytes is Uint8Array[]

Printing to Console

Send output directly to stdout:

await run("ls", "-la").toStdout();

This is perfect for commands where you just want to see the output.

Building Commands Dynamically

Sometimes you need to build a command from variables:

import { type Cmd, run } from "jsr:@j50n/proc@0.24.6";

const cmd: Cmd = ["ls"];

if (showAll) {
  cmd.push("-la");
}

if (directory) {
  cmd.push(directory);
}

await run(...cmd).toStdout();

The Cmd type is an array where the first element is the command (string or URL) and the rest are string arguments. Using the Cmd type ensures type safety when building commands dynamically.

Process Options

Customize process behavior with options:

await run(
  {
    cwd: "/tmp", // Working directory
    env: { FOO: "bar" }, // Environment variables
  },
  "command",
  "arg1",
).lines.collect();

Working Directory

await run(
  { cwd: "/var/log" },
  "ls",
).toStdout();

Environment Variables

await run(
  { env: { PATH: "/custom/path" } },
  "command",
).lines.collect();

Checking Exit Status

Get the exit status without throwing:

const p = run("command");
await p.lines.collect(); // Consume output first
const status = await p.status;

console.log(`Exit code: ${status.code}`);
console.log(`Success: ${status.success}`);

Remember: Always consume output before checking status, or you’ll leak resources.

Process ID

Get the process ID:

const p = run("sleep", "10");
console.log(`PID: ${p.pid}`);
await p.lines.collect();

Running with URLs

You can use URLs for the command:

const scriptUrl = new URL("./script.sh", import.meta.url);
await run(scriptUrl).toStdout();

Common Patterns

Silent Execution

Run a command and ignore output:

await run("command").lines.forEach(() => {});

Capture and Print

Capture output while also printing it:

const lines: string[] = [];
await run("command").lines.forEach((line) => {
  console.log(line);
  lines.push(line);
});

Conditional Execution

if (needsProcessing) {
  await run("process-data").toStdout();
}

Error Handling

By default, non-zero exit codes throw ExitCodeError:

try {
  await run("false").lines.collect();
} catch (error) {
  console.error(`Command failed: ${error.code}`);
}

See Error Handling for complete details.

Performance Tips

Stream Instead of Collect

Process data as it arrives rather than loading everything into memory:

// ❌ Loads everything into memory
const lines = await run("cat", "huge-file.txt").lines.collect();
for (const line of lines) {
  process(line);
}

// ✅ Processes one line at a time
for await (const line of run("cat", "huge-file.txt").lines) {
  process(line);
}

Pipe Instead of Collect Intermediate Results

Chain processes instead of collecting intermediate results:

// ❌ Collects intermediate results
const lines1 = await run("cat", "file.txt").lines.collect();
const input = lines1.join("\n");
const lines2 = await run("grep", "pattern").lines.collect();

// ✅ Streams through
await run("cat", "file.txt")
  .run("grep", "pattern")
  .toStdout();

Use take() to Stop Early

Stop processing once you have what you need:

// Stops after finding 10 matches
const first10 = await run("grep", "ERROR", "huge.log")
  .lines
  .take(10)
  .collect();

Filter Before Expensive Operations

Reduce the amount of data flowing through expensive operations:

// ✅ Filter first (fast), then transform (expensive)
const result = await run("cat", "data.txt")
  .lines
  .filter((line) => line.length > 0) // Fast filter
  .map(expensiveTransform) // Only runs on filtered data
  .collect();

For more performance optimization strategies, see Concurrent Processing and Streaming Large Files.

See Also