Bun Shell: Cross-Platform Scripting with $`...`
Bun Shell exposes a $command tagged template in JavaScript, running shell-like pipelines portably without bash.
Bun Shell lets you write build and CI scripts in TypeScript instead of bash, with the same commands working on Linux, macOS, and Windows. It handles escaping and piping safely.
What it does
Importing { $ } from "bun" gives a tagged-template function that runs commands through Bun's built-in cross-platform shell. It supports pipes, redirection, and common builtins (cd, ls, echo, rm) implemented in Bun, so scripts behave the same regardless of the host OS. Interpolated values are escaped automatically.
Common usage
import { $ } from "bun";
await $`echo Hello`;
const out = await $`ls -1`.text();
await $`mkdir -p dist && cp index.html dist/`;
// interpolation is escaped safely
const name = "my file.txt";
await $`touch ${name}`;Options
| Form | What it does |
|---|---|
$cmd | Run a command via the cross-platform shell |
| .text() | Resolve stdout as a string |
| .quiet() | Suppress forwarding output to the terminal |
| ${value} | Safely interpolated and escaped argument |
| .cwd(dir) | Run in a specific working directory |
In CI
Bun Shell is handy when CI runs on mixed OSes (Linux runners plus Windows) and you want one script instead of separate bash and PowerShell versions. Run it with bun run script.ts. Because interpolation is escaped, it avoids a class of injection bugs that raw bash string-building invites.
Common errors in CI
"command not found" inside $... for an external tool means the binary is not on PATH on that runner; Bun implements common builtins but shells out for the rest. A non-zero exit throws a ShellError by default; wrap in try/catch or use .nothrow() if a non-zero code is acceptable. Bash-specific syntax that Bun Shell does not implement will not parse; stick to the supported subset.