DEV Community

Alex Spinov
Alex Spinov

Posted on

Bun Shell Has a Built-in Shell Scripting Language — Here's How to Use It

Bun introduced something most JavaScript developers never expected — a built-in shell scripting language that runs cross-platform.

What is Bun Shell?

Bun Shell (Bun.$) is a cross-platform shell built into the Bun runtime. It lets you run shell commands using tagged template literals with full JavaScript interop.

Quick Start

import { $ } from "bun";

// Run any shell command
await $`echo Hello from Bun Shell!`;

// Capture output
const result = await $`ls -la`.text();
console.log(result);

// Use JavaScript variables seamlessly
const dir = "./src";
await $`find ${dir} -name "*.ts" | wc -l`;
Enter fullscreen mode Exit fullscreen mode

Why Bun Shell Matters

1. Cross-Platform by Default

// This works on macOS, Linux, AND Windows
await $`echo $HOME`; // Uses Bun's built-in shell, not system shell
await $`cat file.txt | grep pattern`;
Enter fullscreen mode Exit fullscreen mode

2. JavaScript Interop

const files = ["a.txt", "b.txt", "c.txt"];
for (const file of files) {
  await $`cat ${file} >> combined.txt`;
}

// Pipe to JavaScript
const output = await $`curl -s https://api.example.com/data`.json();
console.log(output.results);
Enter fullscreen mode Exit fullscreen mode

3. Error Handling

try {
  await $`exit 1`;
} catch (err) {
  console.log(err.exitCode); // 1
  console.log(err.stderr.toString());
}

// Or use .nothrow() to suppress errors
const result = await $`maybe-missing-command`.nothrow();
if (result.exitCode !== 0) {
  console.log("Command failed, but we handled it");
}
Enter fullscreen mode Exit fullscreen mode

4. Piping and Redirects

// Pipe between commands
await $`cat data.json | jq .items[] | sort | uniq`;

// Redirect to file
await $`echo "Hello" > output.txt`;
await $`echo "World" >> output.txt`;

// Pipe to Response (for HTTP servers)
const server = Bun.serve({
  fetch(req) {
    return new Response($`cat large-file.txt`.stdout);
  },
});
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

Build Scripts

// build.ts
import { $ } from "bun";

await $`rm -rf dist`;
await $`bun build ./src/index.ts --outdir ./dist`;
await $`cp -r public/* dist/`;
console.log("Build complete!");
Enter fullscreen mode Exit fullscreen mode

Database Backup Script

import { $ } from "bun";

const date = new Date().toISOString().split("T")[0];
const dbUrl = process.env.DATABASE_URL;

await $`pg_dump ${dbUrl} | gzip > backups/db-${date}.sql.gz`;
await $`echo Backup created: backups/db-${date}.sql.gz`;
Enter fullscreen mode Exit fullscreen mode

Dev Environment Setup

import { $ } from "bun";

// Check dependencies
await $`docker compose up -d`;
await $`bun install`;
await $`bun run db:migrate`;
await $`echo Dev environment ready!`;
Enter fullscreen mode Exit fullscreen mode

Bun Shell vs Alternatives

Feature Bun Shell zx (Google) execa child_process
Cross-platform Yes Partial Yes No
Template literals Yes Yes No No
Built-in (no install) Yes No No Yes
JavaScript interop Native Good Good Manual
Performance Fast Moderate Good Varies

Performance

Bun Shell is significantly faster than spawning system shells because it uses Bun's built-in shell implementation. No fork/exec overhead for basic operations.


Building automation scripts for web scraping? Check out my ready-to-use scraping actors on Apify Store — get clean data in minutes without writing code. For custom solutions, email spinov001@gmail.com.

Top comments (0)