A tool for writing better scripts by bridging JavaScript and shell commands with cross-platform wrappers around child_process
Advanced process control including promises, output handling, piping, and process lifecycle management through ProcessPromise and ProcessOutput classes.
Extended Promise for handling shell command execution with additional process control methods.
/**
* Promise-like class for shell command execution with process control
*/
class ProcessPromise extends Promise<ProcessOutput> {
/** Start the process execution */
run(): this;
/** Kill the process with optional signal */
kill(signal?: NodeJS.Signals | null): Promise<void>;
/** Abort the process execution */
abort(reason?: string): void;
/** Configure whether to throw on non-zero exit codes */
nothrow(v?: boolean): this;
/** Configure output verbosity */
quiet(v?: boolean): this;
/** Configure verbose output */
verbose(v?: boolean): this;
/** Set execution timeout */
timeout(d?: Duration, signal?: NodeJS.Signals | undefined): this;
/** @deprecated Use $({halt: true})`cmd` instead */
halt(): this;
/** Configure standard input/output */
stdio(stdin: IOType | StdioOptions, stdout?: IOType, stderr?: IOType): this;
}Process Information Properties:
class ProcessPromise extends Promise<ProcessOutput> {
/** Unique process identifier */
get id(): string;
/** Process ID (available after execution starts) */
get pid(): number | undefined;
/** Command string without arguments */
get cmd(): string;
/** Full command string with arguments */
get fullCmd(): string;
/** Child process instance */
get child(): ChildProcess | undefined;
/** Process execution stage */
get stage(): ProcessStage;
/** Whether execution is synchronous */
get sync(): boolean;
/** Abort signal */
get signal(): AbortSignal;
/** Abort controller */
get ac(): AbortController;
/** Process output (available after completion) */
get output(): ProcessOutput | null;
}
type ProcessStage = 'initial' | 'halted' | 'running' | 'fulfilled' | 'rejected';Stream Access:
class ProcessPromise extends Promise<ProcessOutput> {
/** Standard input stream */
get stdin(): Writable;
/** Standard output stream */
get stdout(): Readable;
/** Standard error stream */
get stderr(): Readable;
/** Promise that resolves with exit code */
get exitCode(): Promise<number | null>;
}Output Processing Methods:
class ProcessPromise extends Promise<ProcessOutput> {
/** Parse output as JSON */
json<T = any>(): Promise<T>;
/** Get output as text string */
text(encoding?: Encoding): Promise<string>;
/** Split output into lines */
lines(delimiter?: Options['delimiter']): Promise<string[]>;
/** Get output as Buffer */
buffer(): Promise<Buffer>;
/** Get output as Blob */
blob(type?: string): Promise<Blob>;
}State Check Methods:
class ProcessPromise extends Promise<ProcessOutput> {
/** Check if process output is suppressed */
isQuiet(): boolean;
/** Check if verbose output is enabled */
isVerbose(): boolean;
/** Check if process won't throw on errors */
isNothrow(): boolean;
/** Check if process is halted */
isHalted(): boolean;
}Usage Examples:
import { $ } from "zx";
// Basic process control
const proc = $`sleep 10`;
console.log(proc.id); // Process identifier
console.log(proc.pid); // Process PID (after started)
// Timeout and error handling
const result = await $`some-command`
.timeout('30s')
.nothrow()
.quiet();
// Stream access
const longProc = $`tail -f /var/log/system.log`;
longProc.stdout.on('data', (chunk) => {
console.log('Log:', chunk.toString());
});
// Kill process
await longProc.kill('SIGTERM');Result object containing command output, exit information, and processing methods.
/**
* Command execution result with output data and metadata
*/
class ProcessOutput extends Error {
/** Exit code (null if killed by signal) */
readonly exitCode: number | null;
/** Signal that killed the process (null if exited normally) */
readonly signal: NodeJS.Signals | null;
/** Standard output content */
readonly stdout: string;
/** Standard error content */
readonly stderr: string;
/** Combined stdout and stderr */
readonly stdall: string;
/** Execution duration in milliseconds */
readonly duration: number;
/** Whether command succeeded (exit code 0) */
readonly ok: boolean;
/** Error message */
readonly message: string;
/** Cause error (if any) */
readonly cause: Error | null;
}Output Processing Methods:
class ProcessOutput extends Error {
/** Parse stdout as JSON */
json<T = any>(): T;
/** Get stdout as text with optional encoding */
text(encoding?: Encoding): string;
/** Split stdout into lines */
lines(delimiter?: string | RegExp): string[];
/** Get stdout as Buffer */
buffer(): Buffer;
/** Get stdout as Blob */
blob(type?: string): Blob;
/** String representation (returns stdout) */
toString(): string;
/** Primitive value (returns stdout) */
valueOf(): string;
/** Iterator over lines */
[Symbol.iterator](dlmtr?: Options['delimiter']): Iterator<string>;
}Static Methods:
class ProcessOutput extends Error {
/** Format exit error message */
static getExitMessage: typeof Fail.formatExitMessage;
/** Format general error message */
static getErrorMessage: typeof Fail.formatErrorMessage;
/** Format error details */
static getErrorDetails: typeof Fail.formatErrorDetails;
/** Get exit code information */
static getExitCodeInfo: typeof Fail.getExitCodeInfo;
/** Create ProcessOutput from generic Error */
static fromError(error: Error): ProcessOutput;
}Usage Examples:
import { $ } from "zx";
// Basic output handling
const result = await $`ls -la`;
console.log(result.stdout); // File listing
console.log(result.exitCode); // 0 for success
console.log(result.ok); // true for success
// Error handling
try {
await $`false`; // Command that always fails
} catch (error) {
if (error instanceof ProcessOutput) {
console.log('Exit code:', error.exitCode);
console.log('stderr:', error.stderr);
console.log('Duration:', error.duration, 'ms');
}
}
// JSON parsing
const packageInfo = await $`cat package.json`;
const pkg = packageInfo.json();
console.log(pkg.name);
// Line processing
const logLines = await $`tail -n 10 /var/log/system.log`;
for (const line of logLines.lines()) {
console.log('Log line:', line);
}Connect processes together using Unix-style piping.
interface ProcessPromise extends Promise<ProcessOutput> {
/** Pipe output to another command or stream */
get pipe(): PipeMethod & {
[key in keyof TSpawnStore]: PipeMethod;
};
/** Remove piping connection */
unpipe(to?: PipeAcceptor): this;
}
type PipeMethod = {
/** Pipe to shell command */
(dest: TemplateStringsArray, ...args: any[]): ProcessPromise;
/** Pipe to file */
(file: string): PromisifiedStream;
/** Pipe to writable stream */
<D extends Writable>(dest: D): PromisifiedStream<D>;
/** Pipe to another ProcessPromise */
<D extends ProcessPromise>(dest: D): D;
};
type PipeAcceptor = Writable | ProcessPromise;
type PromisifiedStream<D extends Writable = Writable> =
D & PromiseLike<ProcessOutput & D> & {
run(): void;
};Usage Examples:
import { $ } from "zx";
import fs from "fs";
// Pipe to another command
const result = await $`cat /etc/passwd`
.pipe`grep root`
.pipe`wc -l`;
// Pipe to file
await $`echo "Hello World"`
.pipe('./output.txt');
// Pipe to stream
const writeStream = fs.createWriteStream('./data.txt');
await $`ls -la`.pipe(writeStream);
// Complex piping
await $`find /var/log -name "*.log"`
.pipe`head -10`
.pipe`sort`
.pipe('./sorted-logs.txt');Process output as an async iterator for streaming data processing.
interface ProcessPromise extends Promise<ProcessOutput> {
/** Async iterator over output lines */
[Symbol.asyncIterator](): AsyncIterator<string>;
}Usage:
import { $ } from "zx";
// Stream processing
const proc = $`tail -f /var/log/system.log`;
for await (const line of proc) {
console.log('New log line:', line);
if (line.includes('ERROR')) {
proc.kill();
break;
}
}type Duration = string | number;
type IOType = 'pipe' | 'ignore' | 'inherit';
type StdioOptions = IOType | Array<IOType | Stream | number | null | undefined>;
type Encoding = BufferEncoding;
interface TSpawnStore {
[key: string]: any;
}Install with Tessl CLI
npx tessl i tessl/npm-zx