Bun Shell 使得使用 JavaScript 和 TypeScript 编写 Shell 脚本变得有趣。它是一个跨平台的类 Bash Shell,具有无缝的 JavaScript 互操作性。
快速入门
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,支持重定向、管道、环境变量等。
- Glob:原生支持 Glob 模式,包括
**
、*
、{expansion}
等。 - 模板字面量:模板字面量用于执行 Shell 命令。这允许轻松插值变量和表达式。
- 安全性:Bun Shell 默认转义所有字符串,防止 Shell 注入攻击。
- JavaScript 互操作性:将
Response
、ArrayBuffer
、Blob
、Bun.file(path)
和其他 JavaScript 对象用作标准输入、标准输出和标准错误。 - Shell 脚本:Bun Shell 可用于运行 Shell 脚本(
.bun.sh
文件)。 - 自定义解释器:Bun Shell 使用 Zig 编写,包括其词法分析器、解析器和解释器。Bun Shell 是一种小型编程语言。
入门
最简单的 Shell 命令是 echo
。要运行它,请使用 $
模板字面量标记
import { $ } from "bun";
await $`echo "Hello World!"`; // Hello World!
默认情况下,Shell 命令打印到标准输出。要静默输出,请调用 .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
将返回标准输出和标准错误作为 Buffer
。
import { $ } from "bun";
const { stdout, stderr } = await $`echo "Hello World!"`.quiet();
console.log(stdout); // Buffer(6) [ 72, 101, 108, 108, 111, 32 ]
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(); // equivilent 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 运算符重定向命令的输入或输出
<
重定向标准输入>
或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
环境变量
环境变量可以像在 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 中运行 buncat
touch
mkdir
which
mv
exit
true
false
yes
seq
dirname
basename
部分 实现
mv
:移动文件和目录(缺少跨设备支持)
尚未 实现,但已规划
- 有关完整列表,请参阅 https://github.com/oven-sh/bun/issues/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
。
为此,只需使用 bun
运行具有 .sh
扩展名的文件。
echo "Hello World! pwd=$(pwd)"
bun ./script.sh
Hello World! pwd=/home/demo
使用 Bun Shell 的脚本是跨平台的,这意味着它们可以在 Windows 上运行
bun .\script.sh
Hello World! pwd=C:\Users\Demo
实现说明
Bun Shell 是 Bun 中的一种小型编程语言,它在 Zig 中实现。它包括手写的词法分析器、解析器和解释器。与 bash、zsh 和其他 shell 不同,Bun Shell 并行运行操作。