我40岁了, 还在写程序。
JS 想调 Python? Bun.$ 轻松搞定。
bash
// 1.js
import { $ } from "bun";
const result = await $`py -c "print('Hello from Python!')"`.text();
console.log(result);
三行代码, Python 就这么跑起来了。bun 1.js 走起, 终端出 Hello from Python!。
今天你会学到这些关键词
| 关键词 | 解释 |
|---|---|
| Bun.$ | Bun 内置的 shell 模板, 反引号里写命令直接跑 |
| ShellError | 子进程跑挂时抛的错, 带退出码和 stderr |
| top-level await | Bun 支持模块顶层直接 await, 不用包 async |
| 子进程 | 操作系统层面的进程, 跟父进程相互独立 |
| 环境变量 | 进程间传值的方式, Python 用 os.environ 读 |
凭啥能调 Python
没啥神秘的。
Python 本身就是个可执行程序。Windows 上叫 py, Mac / Linux 上叫 python3。
你打开终端敲 py hello.py, 系统就找到 Python 解释器, 把脚本喂给它跑。
Bun.$ 干的就是这事儿。区别只是: 不用打开终端, 在 JS 文件里写反引号就行。
例 1: 调外部 .py 文件
hello.py:
bash
# hello.py
import math
import sys
import io
# 强制 stdout 用 UTF-8, 避免 Windows 终端乱码
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
def circle_area(radius: float) -> float:
"""根据半径计算圆的面积"""
return math.pi * radius ** 2
if __name__ == "__main__":
r = 5
area = circle_area(r)
print(f"半径 {r} 的圆, 面积是 {area:.2f}")
2.js:
bash
// 2.js
import { $ } from "bun";
try {
const output = await $`py hello.py`.text();
console.log(output);
} catch (error) {
console.error("Python 脚本执行出错:", error);
}
跑一下:
bash
bun 2.js
输出:
bash
半径 5 的圆, 面积是 78.54
JS 这头一个 await, Python 那头 math.pi 就给你安排上了。整个流程: JS 发命令, Python 接活儿, 结果回传 JS。丝滑得很。
例 2: 给 Python 传参数
JS 这头有数据, 想喂给 Python 处理, 咋整?
最稳的方式是走环境变量 。.env() 把变量塞进子进程, Python 用 os.environ 接。安全, 简单, 跨平台。
3.js:
bash
// 3.js
import { $ } from "bun";
try {
const number = 7;
const output = await $`py calc.py`
.env({ NUMBER: String(number) })
.text();
console.log(output);
} catch (error) {
console.error("出错:", error);
}
calc.py:
bash
import sys
import io
import os
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
n = int(os.environ.get("NUMBER", "0"))
print(f"输入 {n}, 平方是 {n ** 2}")
跑一下:
bash
bun 3.js
输出:
bash
输入 7, 平方是 49
数据过去, 结果回来, 干净利落。
例 3: 接 JSON 返回值
Python 算完想回传给 JS?
让它吐 JSON, Bun 这头 .json() 一接, 完事儿。跨语言传结构化数据就这么简单。
4.js:
bash
// 4.js
import { $ } from "bun";
try {
const data = await $`py data.py`.json();
console.log("姓名:", data.user.name);
console.log("年龄:", data.user.age);
} catch (error) {
console.error("出错:", error);
}
data.py:
bash
import sys
import io
import json
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
result = {"user": {"name": "老王", "age": 40}}
print(json.dumps(result, ensure_ascii=False))
跑一下:
bash
bun 4.js
输出:
bash
姓名: 老王
年龄: 40
Python 输出 JSON, JS 这头直接拿到对象。直接 data.user.name 用, 不用解析, 丝滑得很。
例 4: 错误处理
Python 报错退出, Bun 这头会抛 ShellError。exitCode 是退出码, stderr 是错误输出。调试的时候, 这俩最有用。
5.js:
bash
// 5.js
import { $ } from "bun";
try {
await $`py error.py`.text();
} catch (error) {
console.error("退出码:", error.exitCode);
console.error("错误信息:", error.stderr.toString());
}
error.py:
bash
# error.py
import sys
sys.stderr.write("出大事了\n")
sys.exit(1)
跑一下:
bash
bun 5.js
输出:
bash
退出码: 1
错误信息: 出大事了
报错信息一清二楚, 排错轻松。
Windows 编码坑
我头回在 Windows 上跑, 输出长这样:
bash
�뾶 5 ��Բ������� 78.54
啥玩意儿? 全是问号方块。
一查才知道, Windows PowerShell 默认 GBK 编码, Python 输出 UTF-8, 字符集对不上, 就糊了。
修法贼简单, 在 Python 脚本顶上加上这几行:
bash
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
把 stdout 强行套成 UTF-8, 谁调都不乱。这几行放任何 Python 脚本顶上都能用, 建议收藏。
写在最后
Bun.$ 这玩意儿, 用起来就一个字: 爽。
以前想在 JS 里调 Python, 得开 child_process, 拼命令, 处理流, 解析输出, 一通操作猛如虎。
现在? 一个反引号, 完事儿。