DEV Community

Alex Spinov
Alex Spinov

Posted on

Bun Shell Has a Free API That Replaces Bash Scripts With TypeScript

Bun Shell (Bun.$) is a built-in shell that lets you write cross-platform shell scripts in TypeScript. No child_process. No exec. Just template literals.

Basic Usage: Shell as Template Literals

import { $ } from "bun";

// Run any command
await $`echo Hello, World!`;

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

// Get as JSON
const pkg = await $`cat package.json`.json();
console.log(pkg.name);

// Get as lines
const files = await $`find . -name "*.ts"`.lines();
Enter fullscreen mode Exit fullscreen mode

Variables: Safe Interpolation

const dir = "/tmp/my-project";
const branch = "main";

// Variables are automatically escaped — NO injection!
await $`mkdir -p ${dir}`;
await $`git clone --branch ${branch} https://github.com/user/repo.git ${dir}`;

// Arrays become multiple arguments
const files = ["file1.ts", "file2.ts", "file3.ts"];
await $`prettier --write ${files}`;
Enter fullscreen mode Exit fullscreen mode

Pipes and Redirects

// Pipe between commands
await $`cat data.csv | grep "error" | wc -l`;

// Redirect to file
await $`echo "hello" > output.txt`;
await $`echo "world" >> output.txt`;

// Pipe to Bun APIs
const response = await fetch("https://api.example.com/data");
await $`jq '.items[]' < ${response}`;
Enter fullscreen mode Exit fullscreen mode

Error Handling

import { $ } from "bun";

// Throws on non-zero exit
try {
  await $`exit 1`;
} catch (e) {
  console.log(e.exitCode); // 1
  console.log(e.stderr.toString());
}

// Quiet mode — don't print to stdout
const result = await $`ls -la`.quiet();

// Nothrow — don't throw on error
const r = await $`command-that-might-fail`.nothrow();
if (r.exitCode !== 0) console.log("Failed, but continuing...");
Enter fullscreen mode Exit fullscreen mode

Environment Variables

await $`NODE_ENV=production bun run build`;

// Or set for all commands
$.env({ NODE_ENV: "production", API_KEY: "secret" });
await $`bun run build`;
Enter fullscreen mode Exit fullscreen mode

Real-World: Build Scripts

#!/usr/bin/env bun
import { $ } from "bun";

// Build pipeline
console.log("Building...");
await $`bun run typecheck`;
await $`bun build ./src/index.ts --outdir ./dist --minify`;

// Run tests
const testResult = await $`bun test`.nothrow();
if (testResult.exitCode !== 0) {
  console.error("Tests failed!");
  process.exit(1);
}

// Deploy
const version = await $`git describe --tags`.text();
await $`docker build -t myapp:${version.trim()} .`;
await $`docker push myapp:${version.trim()}`;

console.log("Deployed version:", version.trim());
Enter fullscreen mode Exit fullscreen mode

Bun APIs: Beyond Shell

// File I/O — fastest in JS
const file = Bun.file("data.json");
const data = await file.json();
await Bun.write("output.csv", csvData);

// HTTP server
Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello!");
  },
});

// SQLite — built-in
import { Database } from "bun:sqlite";
const db = new Database("scraping.db");
db.run("CREATE TABLE IF NOT EXISTS results (url TEXT, data TEXT)");
Enter fullscreen mode Exit fullscreen mode

Build scraping scripts with Bun? My Apify tools handle the heavy lifting.

Custom Bun-powered solution? Email spinov001@gmail.com

Top comments (0)