or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

database.mdffi.mdfile-system.mdglobals.mdhtml-rewriter.mdhttp-server.mdindex.mdprocess-management.mdredis.mds3.mdshell.mdtesting.mdutilities.md
tile.json

shell.mddocs/

Shell Scripting

Enhanced shell scripting with template literals, cross-platform compatibility, streaming I/O, and comprehensive process control for modern JavaScript shell operations.

Core Imports

import { $ } from "bun";

Capabilities

Shell Template Literals

Execute shell commands using template literal syntax with automatic escaping, expression interpolation, and promise-based execution.

/**
 * Execute shell commands with template literal syntax
 * @param strings - Template strings array
 * @param expressions - Interpolated expressions
 * @returns ShellPromise for command execution
 */
function $(strings: TemplateStringsArray, ...expressions: ShellExpression[]): $.ShellPromise;

type ShellExpression = 
  | { toString(): string }
  | Array<ShellExpression>
  | string
  | { raw: string }
  | Subprocess
  | ReadableStream
  | WritableStream;

Usage Examples:

import { $ } from "bun";

// Basic command execution
const result = await $`echo "Hello World"`;
console.log(result.stdout.toString()); // "Hello World\n"

// Variable interpolation with automatic escaping
const filename = "my file.txt";
await $`touch ${filename}`; // Automatically escapes spaces

// Raw string interpolation (no escaping)
const command = "ls -la";
await $`${$.raw(command)}`; // Executes as literal command

// Expression interpolation
const files = ["file1.txt", "file2.txt"];
await $`echo ${files.join(" ")}`; // "file1.txt file2.txt"

// Piping between commands
const output = await $`ls -la | grep ".txt" | wc -l`;
console.log(`Found ${output.text().trim()} .txt files`);

Shell Configuration

Configure shell behavior including environment variables, working directory, error handling, and output control.

/**
 * Shell configuration methods for customizing execution environment
 */
namespace $ {
  /**
   * Perform bash-like brace expansion on patterns
   * @param pattern - Brace pattern to expand
   * @returns Array of expanded strings
   */
  function braces(pattern: string): string[];
  
  /**
   * Escape strings for safe input into shell commands
   * @param input - String to escape
   * @returns Escaped string safe for shell execution
   */
  function escape(input: string): string;
  
  /**
   * Configure default environment variables for shells
   * @param newEnv - Default environment variables
   * @returns Configured $ instance
   */
  function env(newEnv?: Record<string, string | undefined>): $;
  
  /**
   * Configure default working directory for shells
   * @param newCwd - Default working directory
   * @returns Configured $ instance
   */
  function cwd(newCwd?: string): $;
  
  /**
   * Configure shell to not throw exceptions on non-zero exit codes
   * @returns Configured $ instance
   */
  function nothrow(): $;
  
  /**
   * Configure whether shell should throw exceptions on non-zero exit codes
   * @param shouldThrow - Whether to throw on non-zero exit codes
   * @returns Configured $ instance
   */
  function throws(shouldThrow: boolean): $;
}

Usage Examples:

// Environment variable configuration
const customShell = $.env({ 
  PATH: "/usr/local/bin:/usr/bin:/bin",
  NODE_ENV: "production"
});

await customShell`echo $NODE_ENV`; // "production"

// Working directory configuration
const projectShell = $.cwd("/path/to/project");
await projectShell`pwd`; // "/path/to/project"

// Error handling configuration
const safeShell = $.nothrow();
const result = await safeShell`exit 1`; // Won't throw, returns result with exitCode: 1

// Brace expansion
const patterns = $.braces("file.{js,ts,jsx,tsx}");
console.log(patterns); // ["file.js", "file.ts", "file.jsx", "file.tsx"]

// String escaping
const userInput = "'; rm -rf /; echo '";
const safe = $.escape(userInput);
await $`echo ${$.raw(safe)}`; // Safely escaped

Shell Promise API

Advanced shell promise interface with streaming I/O, output formatting, and execution control for complex shell operations.

/**
 * Shell promise class for executing and controlling shell commands
 */
class ShellPromise extends Promise<ShellOutput> {
  /** Writable stream for stdin input */
  get stdin(): WritableStream;
  
  /**
   * Change working directory for this command
   * @param newCwd - New working directory
   * @returns ShellPromise instance for chaining
   */
  cwd(newCwd: string): this;
  
  /**
   * Set environment variables for this command
   * @param newEnv - Environment variables
   * @returns ShellPromise instance for chaining
   */
  env(newEnv: Record<string, string> | undefined): this;
  
  /**
   * Configure command to only buffer output (no echoing to console)
   * @returns ShellPromise instance for chaining
   */
  quiet(): this;
  
  /**
   * Read stdout line by line as an async iterable
   * @returns AsyncIterable of stdout lines
   */
  lines(): AsyncIterable<string>;
  
  /**
   * Read stdout as a string with optional encoding
   * @param encoding - Text encoding for output
   * @returns Promise resolving to stdout string
   */
  text(encoding?: BufferEncoding): Promise<string>;
  
  /**
   * Read stdout as parsed JSON object
   * @returns Promise resolving to parsed JSON
   */
  json(): Promise<any>;
  
  /**
   * Read stdout as ArrayBuffer
   * @returns Promise resolving to ArrayBuffer
   */
  arrayBuffer(): Promise<ArrayBuffer>;
  
  /**
   * Read stdout as Blob
   * @returns Promise resolving to Blob
   */
  blob(): Promise<Blob>;
  
  /**
   * Configure this command to not throw on non-zero exit codes
   * @returns ShellPromise instance for chaining
   */
  nothrow(): this;
  
  /**
   * Configure whether this command should throw on non-zero exit codes
   * @param shouldThrow - Whether to throw on non-zero exit codes
   * @returns ShellPromise instance for chaining
   */
  throws(shouldThrow: boolean): this;
}

interface ShellOutput {
  readonly stdout: Buffer;
  readonly stderr: Buffer;
  readonly exitCode: number;
  
  text(encoding?: BufferEncoding): string;
  json(): any;
  arrayBuffer(): ArrayBuffer;
  blob(): Blob;
}

Usage Examples:

// Streaming output processing
const command = $`find . -name "*.js" -type f`;

// Process lines as they come
for await (const line of command.quiet().lines()) {
  console.log(`Found: ${line}`);
}

// Different output formats
const textOutput = await $`cat package.json`.text();
const jsonOutput = await $`cat package.json`.json();
const binaryOutput = await $`cat image.png`.arrayBuffer();

// Environment and directory configuration
const result = await $`pwd`
  .cwd("/tmp")
  .env({ CUSTOM_VAR: "value" })
  .quiet();

// Error handling with nothrow
const safeResult = await $`ls /nonexistent`.nothrow();
if (safeResult.exitCode !== 0) {
  console.log("Directory not found:", safeResult.stderr.toString());
}

// Stdin piping
const proc = $`grep "error"`.quiet();
const writer = proc.stdin.getWriter();
await writer.write(new TextEncoder().encode("info: success\nerror: failed\n"));
await writer.close();

const output = await proc.text();
console.log(output); // "error: failed\n"

Error Handling

Comprehensive error handling with detailed error information, output capture, and customizable exception behavior.

/**
 * Shell error class for command execution failures
 */
class ShellError extends Error implements ShellOutput {
  /** Standard output buffer */
  readonly stdout: Buffer;
  /** Standard error buffer */
  readonly stderr: Buffer;
  /** Process exit code */
  readonly exitCode: number;
  
  /**
   * Read stdout as string
   * @param encoding - Text encoding
   * @returns Stdout as string
   */
  text(encoding?: BufferEncoding): string;
  
  /**
   * Read stdout as parsed JSON
   * @returns Parsed JSON object
   */
  json(): any;
  
  /**
   * Read stdout as ArrayBuffer
   * @returns ArrayBuffer of stdout
   */
  arrayBuffer(): ArrayBuffer;
  
  /**
   * Read stdout as Blob
   * @returns Blob of stdout
   */
  blob(): Blob;
}

Usage Examples:

// Error handling with try-catch
try {
  const result = await $`exit 1`;
} catch (error) {
  if (error instanceof $.ShellError) {
    console.log(`Command failed with exit code: ${error.exitCode}`);
    console.log(`Stderr: ${error.stderr.toString()}`);
    console.log(`Stdout: ${error.stdout.toString()}`);
  }
}

// Conditional error handling
const result = await $`grep "pattern" file.txt`.nothrow();

if (result.exitCode === 0) {
  console.log("Pattern found:", result.text());
} else if (result.exitCode === 1) {
  console.log("Pattern not found");
} else {
  console.error("Command failed:", result.stderr.toString());
}

// Complex shell pipelines with error handling
try {
  const pipeline = await $`
    find . -name "*.js" | 
    xargs grep -l "TODO" | 
    head -10
  `.text();
  
  const todoFiles = pipeline.trim().split("\n");
  console.log(`Found TODO comments in ${todoFiles.length} files`);
} catch (error) {
  if (error instanceof $.ShellError) {
    console.error(`Pipeline failed: ${error.message}`);
  }
}

Advanced Shell Operations

Complex shell operations including process substitution, background jobs, and integration with Node.js streams and processes.

// Process substitution and piping
type ShellExpression = 
  | Subprocess<SpawnOptions.Writable, SpawnOptions.Readable, SpawnOptions.Readable>
  | ReadableStream
  | WritableStream;

Usage Examples:

// Process substitution with Bun.spawn
const subprocess = Bun.spawn(["echo", "Hello from subprocess"]);
const result = await $`cat ${subprocess.stdout}`;
console.log(result.text()); // "Hello from subprocess\n"

// Stream piping
const readable = new ReadableStream({
  start(controller) {
    controller.enqueue(new TextEncoder().encode("line 1\nline 2\nline 3\n"));
    controller.close();
  }
});

const processed = await $`grep "2" ${readable}`.text();
console.log(processed); // "line 2\n"

// Complex data processing pipeline
const logAnalysis = await $`
  tail -1000 /var/log/app.log |
  grep ERROR |
  awk '{print $1, $2}' |
  sort |
  uniq -c |
  sort -nr |
  head -5
`.text();

console.log("Top 5 error timestamps:");
console.log(logAnalysis);

// File processing with shell utilities
const files = await $`find . -name "*.json" -type f`.text();
const jsonFiles = files.trim().split("\n");

for (const file of jsonFiles) {
  try {
    const isValid = await $`python -m json.tool ${file}`.nothrow();
    if (isValid.exitCode === 0) {
      console.log(`✓ ${file} is valid JSON`);
    } else {
      console.log(`✗ ${file} has invalid JSON`);
    }
  } catch (error) {
    console.log(`✗ ${file} could not be processed`);
  }
}