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
- Working with Output — Transform and process command output
- Process Pipelines — Chain multiple commands together
- Error Handling — Handle failures gracefully
- Shell Script Replacement — Replace bash scripts with proc