Bun Shell makes shell scripting with JavaScript & TypeScript fun. It's a cross-platform bash-like shell with seamless JavaScript interop.
快速开始
import { $ } from "bun";
const response = await fetch("https://example.com");
// Use Response as stdin.
await $`cat < ${response} | wc -c`; // 1256
特性:
- 跨平台: 适用于 Windows、Linux 和 macOS。您可以使用 Bun Shell,无需安装
rimraf或cross-env等额外依赖项。ls、cd、rm等常用 shell 命令均已原生实现。 - 熟悉: Bun Shell 是一个类似 bash 的 shell,支持重定向、管道、环境变量等。
- Globs: 原生支持 Glob 模式,包括
**、*、{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。要运行它,请使用 $ 模板字面量标签。
import { $ } from "bun";
await $`echo "Hello World!"`; // Hello World!
默认情况下,shell 命令打印到 stdout。要静默输出,请调用 .quiet()。
import { $ } from "bun";
await $`echo "Hello World!"`.quiet(); // No output
如果您想将命令的输出访问为文本,请使用 .text()。
import { $ } from "bun";
// .text() automatically calls .quiet() for you
const welcome = await $`echo "Hello World!"`.text();
console.log(welcome); // Hello World!\n
默认情况下,await 会将 stdout 和 stderr 作为 Buffer 返回。
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 包含有关已运行命令的信息。
import { $ } from "bun";
try {
const output = await $`something-that-may-fail`.text();
console.log(output);
} catch (err) {
console.log(`Failed with code ${err.exitCode}`);
console.log(err.stdout.toString());
console.log(err.stderr.toString());
}
可以通过 .nothrow() 禁用抛出。结果的 exitCode 将需要手动检查。
import { $ } from "bun";
const { stdout, stderr, exitCode } = await $`something-that-may-fail`
.nothrow()
.quiet();
if (exitCode !== 0) {
console.log(`Non-zero exit code ${exitCode}`);
}
console.log(stdout);
console.log(stderr);
可以通过在 $ 函数本身上调用 .nothrow() 或 .throws(boolean) 来配置非零退出码的默认处理。
import { $ } from "bun";
// shell promises will not throw, meaning you will have to
// check for `exitCode` manually on every shell command.
$.nothrow(); // equivalent to $.throws(false)
// default behavior, non-zero exit codes will throw an error
$.throws(true);
// alias for $.nothrow()
$.throws(false);
await $`something-that-may-fail`; // No exception thrown
重定向
命令的输入或输出可以使用典型的 Bash 运算符重定向。
<重定向 stdin>或1>重定向 stdout2>重定向 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 对象重定向。
示例: 将输出重定向到 JavaScript 对象 (>)
要将 stdout 重定向到 JavaScript 对象,请使用 > 运算符。
import { $ } from "bun";
const buffer = Buffer.alloc(100);
await $`echo "Hello World!" > ${buffer}`;
console.log(buffer.toString()); // Hello World!\n
以下 JavaScript 对象支持重定向到:
Buffer、Uint8Array、Uint16Array、Uint32Array、Int8Array、Int16Array、Int32Array、Float32Array、Float64Array、ArrayBuffer、SharedArrayBuffer(写入底层缓冲区)Bun.file(path)、Bun.file(fd)(写入文件)
示例: 将输入重定向自 JavaScript 对象 (<)
要将 JavaScript 对象的输出重定向到 stdin,请使用 < 运算符。
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
以下 JavaScript 对象支持重定向自:
Buffer、Uint8Array、Uint16Array、Uint32Array、Int8Array、Int16Array、Int32Array、Float32Array、Float64Array、ArrayBuffer、SharedArrayBuffer(从底层缓冲区读取)Bun.file(path)、Bun.file(fd)(从文件读取)Response(从主体读取)
示例: 将 stdin 重定向到文件
import { $ } from "bun";
await $`cat < myfile.txt`;
示例: 将 stdout 重定向到文件
import { $ } from "bun";
await $`echo bun! > greeting.txt`;
示例: 将 stderr 重定向到文件
import { $ } from "bun";
await $`bun run index.ts 2> errors.txt`;
示例: 将 stderr 重定向到 stdout
import { $ } from "bun";
// redirects stderr to stdout, so all output
// will be available on stdout
await $`bun run ./index.ts 2>&1`;
示例: 将 stdout 重定向到 stderr
import { $ } from "bun";
// redirects stdout to stderr, so all output
// will be available on stderr
await $`bun run ./index.ts 1>&2`;
管道 (|)
与 bash 一样,您可以将一个命令的输出通过管道传递给另一个命令。
import { $ } from "bun";
const result = await $`echo "Hello World!" | wc -w`.text();
console.log(result); // 2\n
您还可以使用 JavaScript 对象进行管道传输。
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
命令替换 ($(...))
命令替换允许您将另一个脚本的输出替换到当前脚本中。
import { $ } from "bun";
// Prints out the hash of the current commit
await $`echo Hash of current commit: $(git rev-parse HEAD)`;
这是命令输出的文本插入,可用于例如声明 shell 变量。
import { $ } from "bun";
await $`
REV=$(git rev-parse HEAD)
docker built -t myapp:$REV
echo Done building docker image "myapp:$REV"
`;
注意: 由于 Bun 内部使用了输入模板字面量的特殊 raw 属性,因此使用反引号语法进行命令替换将不起作用。
import { $ } from "bun";
await $`echo \`echo hi\``;
而不是打印
hi
上面的代码将打印出
echo hi
我们建议坚持使用 $(...) 语法。
环境变量
可以像在 bash 中一样设置环境变量。
import { $ } from "bun";
await $`FOO=foo bun -e 'console.log(process.env.FOO)'`; // foo\n
您可以使用字符串插值来设置环境变量。
import { $ } from "bun";
const foo = "bar123";
await $`FOO=${foo + "456"} bun -e 'console.log(process.env.FOO)'`; // bar123456\n
默认情况下,输入会被转义,从而防止 shell 注入攻击。
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() 来为单个命令更改环境变量
import { $ } from "bun";
await $`echo $FOO`.env({ ...process.env, FOO: "bar" }); // bar
您可以通过调用 $.env 来更改所有命令的默认环境变量
import { $ } from "bun";
$.env({ FOO: "bar" });
// the globally-set $FOO
await $`echo $FOO`; // bar
// the locally-set $FOO
await $`echo $FOO`.env({ FOO: "baz" }); // baz
您可以通过调用不带参数的 $.env() 将环境变量重置为默认值
import { $ } from "bun";
$.env({ FOO: "bar" });
// the globally-set $FOO
await $`echo $FOO`; // bar
// the locally-set $FOO
await $`echo $FOO`.env(undefined); // ""
更改当前工作目录
您可以通过将字符串传递给 .cwd() 来更改命令的当前工作目录
import { $ } from "bun";
await $`pwd`.cwd("/tmp"); // /tmp
您可以通过调用 $.cwd 来更改所有命令的默认当前工作目录
import { $ } from "bun";
$.cwd("/tmp");
// the globally-set working directory
await $`pwd`; // /tmp
// the locally-set working directory
await $`pwd`.cwd("/"); // /
读取输出
要将命令的输出读取为字符串,请使用 .text()
import { $ } from "bun";
const result = await $`echo "Hello World!"`.text();
console.log(result); // Hello World!\n
将输出读取为 JSON
要将命令的输出读取为 JSON,请使用 .json()
import { $ } from "bun";
const result = await $`echo '{"foo": "bar"}'`.json();
console.log(result); // { foo: "bar" }
逐行读取输出
要逐行读取命令的输出,请使用 .lines()
import { $ } from "bun";
for await (let line of $`echo "Hello World!"`.lines()) {
console.log(line); // Hello World!
}
您也可以在已完成的命令上使用 .lines()
import { $ } from "bun";
const search = "bun";
for await (let line of $`cat list.txt | grep ${search}`.lines()) {
console.log(line);
}
将输出读取为 Blob
要将命令的输出读取为 Blob,请使用 .blob()
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 中运行 Buncattouchmkdirwhichmvexittruefalseyesseqdirnamebasename
部分实现
mv:移动文件和目录(缺少跨设备支持)
尚未实现,但已计划
- 有关完整列表,请参阅 Issue #9716。
实用程序
Bun Shell 还实现了一组用于处理 shell 的实用程序。
$.braces(花括号扩展)
此函数为 shell 命令实现了简单的 花括号扩展
import { $ } from "bun";
await $.braces(`echo {1,2,3}`);
// => ["echo 1", "echo 2", "echo 3"]
$.escape(转义字符串)
将 Bun Shell 的转义逻辑作为函数公开
import { $ } from "bun";
console.log($.escape('$(foo) `bar` "baz"'));
// => \$(foo) \`bar\` \"baz\"
如果您不希望您的字符串被转义,请将其包装在 { raw: 'str' } 对象中
import { $ } from "bun";
await $`echo ${{ raw: '$(foo) `bar` "baz"' }}`;
// => bun: command not found: foo
// => bun: command not found: bar
// => baz
.sh 文件加载器
对于简单的 shell 脚本,您可以使用 Bun Shell 来运行 shell 脚本,而不是使用 /bin/sh。
为此,只需在具有 .sh 扩展名的文件上使用 bun 运行脚本即可。
echo "Hello World! pwd=$(pwd)"
bun ./script.shHello World! pwd=/home/demo使用 Bun Shell 的脚本是跨平台的,这意味着它们可以在 Windows 上运行
bun .\script.shHello World! pwd=C:\Users\Demo实现说明
Bun Shell 是 Bun 中的一种小型编程语言,用 Zig 实现。它包括一个手写的词法分析器、解析器和解释器。与 bash、zsh 和其他 shell 不同,Bun Shell 并发运行操作。
Bun Shell 中的安全性
根据设计,Bun Shell **不调用系统 shell**(如 /bin/sh),而是 在同一个 Bun 进程中重新实现 bash, 并以安全为设计理念。
在解析命令参数时,它将所有*插值变量*视为单个、字面字符串。
这可以保护 Bun Shell 免受**命令注入**
import { $ } from "bun";
const userInput = "my-file.txt; rm -rf /";
// SAFE: `userInput` is treated as a single quoted string
await $`ls ${userInput}`;
在上面的示例中,userInput 被视为单个字符串。这会导致 ls 命令尝试读取名为 "my-file; rm -rf /" 的单个目录的内容。
安全注意事项
虽然默认情况下会阻止命令注入,但在某些情况下,开发人员仍 需负责安全性。
与 Bun.spawn 或 node:child_process.exec() API 类似,您可以故意 执行一个会启动新 shell(例如 bash -c)并带参数的命令。
执行此操作时,您将控制权移交给该新 shell,Bun 内置的保护 将不再适用于该新 shell 解释的字符串。
import { $ } from "bun";
const userInput = "world; touch /tmp/pwned";
// UNSAFE: You have explicitly started a new shell process with `bash -c`.
// This new shell will execute the `touch` command. Any user input
// passed this way must be rigorously sanitized.
await $`bash -c "echo ${userInput}"`;
参数注入
Bun Shell 无法知道外部命令如何解释其自身的 命令行参数。攻击者可以提供目标程序 将其识别为其自己的选项或标志的输入,从而导致意外行为。
import { $ } from "bun";
// Malicious input formatted as a Git command-line flag
const branch = "--upload-pack=echo pwned";
// UNSAFE: While Bun safely passes the string as a single argument,
// the `git` program itself sees and acts upon the malicious flag.
await $`git ls-remote origin ${branch}`;
建议 — 与任何语言中的最佳实践一样,始终对 用户提供的输入进行消毒,然后再将其作为参数传递给外部命令。 验证参数的责任在于您的应用程序代码。