> ## Documentation Index
> Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Shell

> 使用 Bun 的 shell 脚本 API 从 JavaScript 运行 shell 命令

Bun Shell 让使用 JavaScript & TypeScript 编写 shell 脚本变得有趣。它是一个跨平台的类 bash shell，具备无缝的 JavaScript 互操作性。

快速开始：

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const response = await fetch("https://example.com");

// 使用 Response 作为 stdin。
await $`cat < ${response} | wc -c`; // 1256
```

***

## 特性

* **跨平台**：在 Windows、Linux 和 macOS 上可用。你无需安装额外依赖即可使用 Bun Shell 替代 `rimraf` 或 `cross-env`。诸如 `ls`、`cd`、`rm` 等常见 shell 命令以原生方式实现。
* **熟悉感**：Bun Shell 是一个 bash 风格的 shell，支持重定向、管道、环境变量等。
* **通配符**：通配符模式原生支持，包括 `**`、`*`、`{expansion}` 等。
* **模板字面量**：模板字面量用于执行 shell 命令，允许插入变量和表达式。
* **安全性**：Bun Shell 默认会对所有字符串进行转义，从而防止 shell 注入攻击。
* **JavaScript 互操作**：将 `Response`、`ArrayBuffer`、`Blob`、`Bun.file(path)` 以及其他 JavaScript 对象用作 stdin、stdout 和 stderr。
* **Shell 脚本**：Bun Shell 可用于运行 shell 脚本（`.bun.sh` 文件）。
* **自定义解释器**：Bun Shell 使用 Zig 编写，并包含其词法解析器、语法解析器和解释器。Bun Shell 是一种小型编程语言。

***

## 快速入门

最简单的 shell 命令是 `echo`。执行时使用 `$` 模板字面量标签：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`echo "Hello World!"`; // Hello World!
```

默认情况下，shell 命令打印到 stdout。如需关闭输出，可调用 `.quiet()`：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`echo "Hello World!"`.quiet(); // 无输出
```

如果想以文本形式访问命令输出，使用 `.text()`：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

// .text() 会自动调用 .quiet()
const welcome = await $`echo "Hello World!"`.text();

console.log(welcome); // Hello World!\n
```

默认情况下，`await` 返回的是 stdout 和 stderr 的 `Buffer`。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const { stdout, stderr } = await $`echo "Hello!"`.quiet();

console.log(stdout); // Buffer(7) [ 72, 101, 108, 108, 111, 33, 10 ]
console.log(stderr); // Buffer(0) []
```

***

## 错误处理

默认情况下，非零退出码会抛出错误。该 `ShellError` 包含执行命令的信息。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

try {
  const output = await $`something-that-may-fail`.text();
  console.log(output);
} catch (err) {
  console.log(`失败，退出码 ${err.exitCode}`);
  console.log(err.stdout.toString());
  console.log(err.stderr.toString());
}
```

可用 `.nothrow()` 禁用抛出，返回结果的 `exitCode` 需手动检查。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const { stdout, stderr, exitCode } = await $`something-that-may-fail`.nothrow().quiet();

if (exitCode !== 0) {
  console.log(`非零退出码 ${exitCode}`);
}

console.log(stdout);
console.log(stderr);
```

也可通过在 `$` 函数上调用 `.nothrow()` 或 `.throws(boolean)` 来配置默认的非零退出码处理行为。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";
// shell promise 不会抛出异常，需要手动检查 `exitCode`
$.nothrow(); // 等价于 $.throws(false)

// 默认行为，非零退出码会抛错
$.throws(true);

// $.nothrow() 的别名
$.throws(false);

await $`something-that-may-fail`; // 不抛异常
```

***

## 重定向

可使用典型 Bash 操作符重定向命令的 *输入* 和 *输出*：

* `<` 重定向 stdin
* `>` 或 `1>` 重定向 stdout
* `2>` 重定向 stderr
* `&>` 同时重定向 stdout 和 stderr
* `>>` 或 `1>>` 追加重定向 stdout（而非覆盖）
* `2>>` 追加重定向 stderr（而非覆盖）
* `&>>` 追加重定向 stdout 和 stderr（而非覆盖）
* `1>&2` 将 stdout 重定向到 stderr（所有写入 stdout 的内容将进入 stderr）
* `2>&1` 将 stderr 重定向到 stdout（所有写入 stderr 的内容将进入 stdout）

Bun Shell 还支持 JavaScript 对象之间的重定向。

### 示例：重定向输出到 JavaScript 对象 (`>`)

使用 `>` 操作符将 stdout 重定向到 JS 对象：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const buffer = Buffer.alloc(100);
await $`echo "Hello World!" > ${buffer}`;

console.log(buffer.toString()); // Hello World!\n
```

以下 JS 对象支持作为重定向目标：

* `Buffer`、`Uint8Array`、`Uint16Array`、`Uint32Array`、`Int8Array`、`Int16Array`、`Int32Array`、`Float32Array`、`Float64Array`、`ArrayBuffer`、`SharedArrayBuffer`（写入底层缓冲区）
* `Bun.file(path)`、`Bun.file(fd)`（写入文件）

### 示例：重定向输入自 JavaScript 对象 (`<`)

使用 `<` 操作符将 JS 对象作为 stdin 重定向输入：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const response = new Response("hello i am a response body");

const result = await $`cat < ${response}`.text();

console.log(result); // hello i am a response body
```

以下 JS 对象支持从中重定向：

* `Buffer`、`Uint8Array`、`Uint16Array`、`Uint32Array`、`Int8Array`、`Int16Array`、`Int32Array`、`Float32Array`、`Float64Array`、`ArrayBuffer`、`SharedArrayBuffer`（从底层缓冲区读）
* `Bun.file(path)`、`Bun.file(fd)`（从文件读）
* `Response`（从 body 读）

### 示例：重定向 stdin -> 文件

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`cat < myfile.txt`;
```

### 示例：重定向 stdout -> 文件

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`echo bun! > greeting.txt`;
```

### 示例：重定向 stderr -> 文件

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`bun run index.ts 2> errors.txt`;
```

### 示例：重定向 stderr -> stdout

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

// 将 stderr 重定向到 stdout，所有输出将显示在 stdout
await $`bun run ./index.ts 2>&1`;
```

### 示例：重定向 stdout -> stderr

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

// 将 stdout 重定向到 stderr，所有输出将显示在 stderr
await $`bun run ./index.ts 1>&2`;
```

## 管道 (`|`)

和 bash 类似，你可以将一个命令的输出通过管道传给另一个命令：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const result = await $`echo "Hello World!" | wc -w`.text();

console.log(result); // 2\n
```

你也可以用 JavaScript 对象做管道：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const response = new Response("hello i am a response body");

const result = await $`cat < ${response} | wc -w`.text();

console.log(result); // 6\n
```

## 命令替换 (`$(...)`)

命令替换允许将另一个脚本的输出嵌入当前脚本：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

// 打印当前提交的哈希
await $`echo Hash of current commit: $(git rev-parse HEAD)`;
```

这是一种将命令输出作为文本插入的方法，例如声明 shell 变量：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`
  REV=$(git rev-parse HEAD)
  docker built -t myapp:$REV
  echo Done building docker image "myapp:$REV"
`;
```

<Note>
  因为 Bun 内部使用了输入模板字面量的特殊 [`raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#raw_strings) 属性，所以使用反引号进行命令替换不起作用：

  ```ts icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
  import { $ } from "bun";

  await $`echo \`echo hi\``;
  ```

  不会打印：

  ```
  hi
  ```

  而是打印：

  ```
  echo hi
  ```

  我们建议坚持使用 `$(...)` 语法。
</Note>

***

## 环境变量

环境变量设置方式类似 bash：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`FOO=foo bun -e 'console.log(process.env.FOO)'`; // foo\n
```

可以通过字符串插值设置环境变量：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const foo = "bar123";

await $`FOO=${foo + "456"} bun -e 'console.log(process.env.FOO)'`; // bar123456\n
```

输入默认被转义，防止 shell 注入攻击：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const foo = "bar123; rm -rf /tmp";

await $`FOO=${foo} bun -e 'console.log(process.env.FOO)'`; // bar123; rm -rf /tmp\n
```

### 修改环境变量

默认所有命令使用 `process.env` 作为环境变量。

可以通过 `.env()` 为单个命令更改环境变量：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`echo $FOO`.env({ ...process.env, FOO: "bar" }); // bar
```

通过设置 `$.env` 可改变所有命令的默认环境变量：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

$.env({ FOO: "bar" });

// 全局设置的 $FOO
await $`echo $FOO`; // bar

// 局部设置的 $FOO
await $`echo $FOO`.env({ FOO: "baz" }); // baz
```

调用 `$.env()` 并传入无参数可以重置环境变量为默认值：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

$.env({ FOO: "bar" });

// 全局设置的 $FOO
await $`echo $FOO`; // bar

// 局部设置的 $FOO
await $`echo $FOO`.env(undefined); // ""
```

### 修改工作目录

可通过传入字符串给 `.cwd()` 改变单个命令的工作目录：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`pwd`.cwd("/tmp"); // /tmp
```

可通过设置 `$.cwd` 更改所有命令的默认工作目录：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

$.cwd("/tmp");

// 全局设置的工作目录
await $`pwd`; // /tmp

// 局部设置的工作目录
await $`pwd`.cwd("/"); // /
```

***

## 读取输出

使用 `.text()` 以字符串形式读取命令输出：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const result = await $`echo "Hello World!"`.text();

console.log(result); // Hello World!\n
```

### 读取 JSON 格式的输出

使用 `.json()` 读取命令的 JSON 输出：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const result = await $`echo '{"foo": "bar"}'`.json();

console.log(result); // { foo: "bar" }
```

### 按行读取输出

使用 `.lines()` 按行读取命令输出：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

for await (let line of $`echo "Hello World!"`.lines()) {
  console.log(line); // Hello World!
}
```

也能在命令执行完后用 `.lines()`：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const search = "bun";

for await (let line of $`cat list.txt | grep ${search}`.lines()) {
  console.log(line);
}
```

### 读取输出为 Blob

使用 `.blob()` 以 Blob 形式读取输出：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const result = await $`echo "Hello World!"`.blob();

console.log(result); // Blob(13) { size: 13, type: "text/plain" }
```

***

## 内置命令

为保证跨平台兼容，Bun Shell 实现了一套内置命令，并且可以从 PATH 环境变量读取命令。

* `cd`：切换工作目录
* `ls`：列出目录文件
* `rm`：删除文件和目录
* `echo`：打印文本
* `pwd`：打印当前工作目录
* `bun`：在 bun 中运行 bun
* `cat`
* `touch`
* `mkdir`
* `which`
* `mv`
* `exit`
* `true`
* `false`
* `yes`
* `seq`
* `dirname`
* `basename`

**部分实现：**

* `mv`：移动文件和目录（尚缺跨设备支持）

**尚未实现但计划中的：**

* 详见 [Issue #9716](https://github.com/oven-sh/bun/issues/9716)。

***

## 工具函数

Bun Shell 也实现了一些用于处理 shell 的工具函数。

### `$.braces`（花括号扩展）

该函数实现了简单的 [花括号扩展](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html)：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $.braces(`echo {1,2,3}`);
// => ["echo 1", "echo 2", "echo 3"]
```

### `$.escape`（转义字符串）

暴露了 Bun Shell 的字符串转义逻辑：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

console.log($.escape('$(foo) `bar` "baz"'));
// => \$(foo) \`bar\` \"baz\"
```

如果不希望字符串被转义，可将其包裹在 `{ raw: 'str' }` 对象中：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

await $`echo ${{ raw: '$(foo) `bar` "baz"' }}`;
// => bun: command not found: foo
// => bun: command not found: bar
// => baz
```

***

## `.sh` 文件加载器

对于简单的 shell 脚本，可以用 Bun Shell 替代 `/bin/sh` 来执行。

To do so, run the script with `bun` on a file with the `.sh` extension.

```sh script.sh icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
echo "Hello World! pwd=$(pwd)"
```

```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun ./script.sh
```

```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
Hello World! pwd=/home/demo
```

Bun Shell 脚本跨平台，Windows 也支持：

```powershell powershell icon="windows" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun .\script.sh
```

```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
Hello World! pwd=C:\Users\Demo
```

***

## 实现说明

Bun Shell 是一个用 Zig 实现、在 Bun 内的简易编程语言。包含手写词法解析器、语法解析器及解释器。与 bash、zsh 等 shell 不同，Bun Shell 支持并发运行操作。

***

## Bun shell 的安全性

Bun shell 设计时**不调用系统 shell**（如 `/bin/sh`），而是重新实现了 bash，直接在 Bun 进程中运行，注重安全设计。

在解析命令参数时，将所有**插值变量**视为单一字面字符串。

这能防止 Bun shell 出现 **命令注入**：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const userInput = "my-file.txt; rm -rf /";

// 安全：`userInput` 会被视为单个带引号字符串
await $`ls ${userInput}`;
```

以上例中，`userInput` 被作为一个单独字符串传给 `ls`，导致 `ls` 查找名为 `"my-file; rm -rf /"` 的单一目录。

### 安全注意事项

虽然默认防止命令注入，开发者仍需注意某些场景的安全性。

类似于 `Bun.spawn` 或 `node:child_process.exec()`，你可选择用参数在新 shell 中执行命令（例如 `bash -c`）。

此时，你交出了控制权，Bun 内置的保护机制不再适用，字符串将由新 shell 解释执行。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

const userInput = "world; touch /tmp/pwned";

// 不安全：显式调用新 shell 进程 `bash -c`。
// 此新 shell 会执行 `touch` 命令。
// 传入的用户输入必须严格过滤。
await $`bash -c "echo ${userInput}"`;
```

### 参数注入

Bun shell 无法知道外部命令如何解析自己的命令行参数。攻击者可传入被目标程序识别为参数或选项的输入，导致异常行为。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
import { $ } from "bun";

// 恶意输入，格式化为 Git 命令行标志
const branch = "--upload-pack=echo pwned";

// 不安全：Bun 作为单个参数传递字符串
// 但 `git` 程序会识别并执行这个恶意标志。
await $`git ls-remote origin ${branch}`;
```

<Note>
  **建议** — 和所有语言中一样，始终在把用户提供的输入作为外部命令参数传递前进行安全检验。参数验证责任由应用代码承担。
</Note>

***

## 致谢

该 API 很大程度上受到 [zx](https://github.com/google/zx)、[dax](https://github.com/dsherret/dax) 和 [bnx](https://github.com/wobsoriano/bnx) 启发，感谢这些项目的作者。
