译:使用 Bun 执行 Shell 脚本
原文地址(Bun Blog): https://bun.sh/blog/the-bun-shell
作者: jarredsumner
发布时间:2024-01-20
前言
JavaScript 是世界上最流行的脚本语言。
那么为什么在 JavaScript 中执行 Shell 脚本很困难呢?
import { spawnSync } from 'child_process'
// 代码比想象中要稍微复杂一些
const { status, stdout, stderr } = spawnSync('ls', ['-l', '*.js'], {
encoding: 'utf8',
})
你也可以使用内置的 API 来执行类似的操作:
import { readdir } from 'fs/promises';
(await readdir('.', { withFileTypes: true })).filter(a =>
a.name.endsWith('.js'),
)
但是,还是没有 shell 脚本简单:
ls *.js
为什么现有的 shell 无法在 JavaScript 中运行
bash 和 sh 等这些 shell 工具已经存在几十年了。
但是,为什么它们在 JavaScript 中不能很好的工作?
macOS (zsh)、Linux (bash) 和 Windows (cmd) 的 shell 都有所不同,具有不同的语法和不同的命令。每个平台上可用的命令都不同,甚至相同的命令也可能有不同的可选参数和行为。
迄今为止,npm 的解决方案是依靠社区用 JavaScript 实现来填补缺失的命令。
rm -rf 不适用于 Windows
rimraf 是 rm -rf 指令的跨平台实现,每周下载 6000 万次:
FOO=bar <script> 设置环境变量在 Windows 上不生效
不同平台上设置环境变量的方式略有不同。如果不使用 FOO=bar 这种方式,那就是使用 cross-env
which 在 Windows 上是 where
于是另一个周下载量 6000w 的包诞生了:
shell 启动时间也有一点长
创建一个 shell 执行需要多久?
在 Linux x64 Hetzner Arch Linux 机器上,大约需要 7ms:
$ hyperfine --warmup 3 'bash -c "echo hello"' 'sh -c "echo hello"' -N
Benchmark 1: bash -c 'echo hello'
Time (mean ± σ): 7.3 ms ± 1.5 ms [User: 5.1 ms, System: 1.9 ms]
Range (min … max): 1.7 ms … 9.4 ms 529 runs
Benchmark 2: sh -c 'echo hello'
Time (mean ± σ): 7.2 ms ± 1.6 ms [User: 4.8 ms, System: 2.1 ms]
Range (min … max): 1.5 ms … 9.6 ms 327 runs
如果只是想运行单个命令,但是启动 shell 可能比运行命令本身花费更长的时间。如果需要在循环中运行许多命令,那么成本就会升高。
你可以尝试嵌入 shell,但这样就复杂了,而且它们的许可证可能与你的项目不兼容。
这些 polyfill 真的必要吗?
在 2009 - 2016 年的里,JavaScript 还相对较新且处于实验阶段时,依靠社区来填补缺失的功能是很合理的。但现在已经是 2024 年了。JavaScript 已在广泛的使用于服务端开发了。如今,JavaScript 生态系统对需求的理解与 2009 年时完全不同了。
我们其实可以做得更好。
介绍一下 Bun Shell
Bun Shell 是 Bun 提供的一种新的实验性的嵌入式语言和解释器,支持使用 JavaScript 和 TypeScript 编写跨平台运行的 shell 脚本。
import { $ } from 'bun'
// 直接在终端里输出
await $`ls *.js`
// 转为字符串变量
const text = await $`ls *.js`.text()
同时允许你使用 JavaScript 变量:
import { $ } from 'bun'
const resp = await fetch('https://example.com')
const stdout = await $`gzip -c < ${resp}`.arrayBuffer()
出于安全问题考虑,所有模板变量都将被转义:
const filename = 'foo.js; rm -rf /'
// 将会执行指令 `ls 'foo.js; rm -rf /'`
const results = await $`ls ${filename}`
console.log(results.exitCode) // 1
console.log(results.stderr.toString()) // ls: cannot access 'foo.js; rm -rf /': No such file or directory
使用 Bun Shell 感觉就像普通的 JavaScript。允许你将标准输出放入 buffers 中:
import { $ } from 'bun'
const buffer = Buffer.alloc(1024)
await $`ls *.js > ${buffer}`
console.log(buffer.toString('utf8'))
你也可以将输出结果直接写入文件:
import { $, file } from 'bun'
// 当做文件输出
await $`ls *.js > ${file('output.txt')}`
// 或者文件路径字符串
await $`ls *.js > output.txt`
await $`ls *.js > ${'output.txt'}`
你还可以将输出结果通过管道运算符传递给其它命令:
import { $ } from 'bun'
await $`ls *.js | grep foo`
你甚至可以使用 Response 作为标准输入:
import { $ } from 'bun'
const buffer = new Response('bar\n foo\n bar\n foo\n')
await $`grep foo < ${buffer}`
可使用 cd、echo 和 rm 等内置命令:
import { $ } from 'bun'
await $`cd .. && rm -rf node_modules/rimraf`
它可在 Windows、macOS 和 Linux 上运行。我们实现了许多常用命令和功能,如通配符、环境变量、重定向(redirection)、管道(piping)等。
它被设计为简单 shell 脚本的替代品。在 Windows 版 Bun 中,它将为 bun run 中的 package.json “脚本”提供支持。
为了更有趣一点,您还可以将它用作独立的 shell 脚本解释器:
echo "cat package.json" > script.bun.sh
bun script.bun.sh
如何安装?
Bun Shell 内置于 Bun 中。如果已经安装了 Bun v1.0.24 或更高版本,那么你就可以使用它:
bun --version
1.0.24
如果你没有安装Bun,可以使用curl安装:
curl -fsSL https://bun.sh/install | bash
或者使用 npm :
npm install -g bun
使用实践
创建 test.ts 文件,写入如下代码
import { $ } from 'bun'
await $`echo hello world`
const files = await $`ls *.js *.mjs`.text()
console.log(files.split('\n'))
运行脚本
bun test.ts
运行结果 如下
译:使用 Bun 执行 Shell 脚本的更多相关文章
- Linux中执行shell脚本的4种方法总结
bash shell 脚本的方法有多种,现在作个小结.假设我们编写好的shell脚本的文件名为hello.sh,文件位置在/data/shell目录中并已有执行权限. 方法一:切换到shell脚本所在 ...
- Linux中执行shell脚本的4种方法
bash shell 脚本的方法有多种,现在作个小结.假设我们编写好的shell脚本的文件名为hello.sh,文件位置在/data/shell目录中并已有执行权限. 方法一:切换到shell脚本所在 ...
- 执行shell脚本的几种方法及区别
执行shell脚本的几种方法及区别 http://blog.csdn.net/lanxinju/article/details/6032368 (认真看) 注意:如果涉及到脚本之间的调用一定要用 . ...
- Java SSH远程执行Shell脚本实现(转)
前言 此程序需要ganymed-ssh2-build210.jar包(下载地址:http://www.ganymed.ethz.ch/ssh2/) 为了调试方便,可以将\ganymed-ssh2-bu ...
- JAVA远程执行Shell脚本类
1.java远程执行shell脚本类 package com.test.common.utility; import java.io.IOException; import java.io.Input ...
- 转:linux执行shell脚本的方式及一些区别
假设shell脚本文件为hello.sh放在/root目录下.下面介绍几种在终端执行shell脚本的方法: [root@localhost home]# cd /root/ [root@localho ...
- Java实践 — SSH远程执行Shell脚本(转)
原文地址:http://www.open-open.com/lib/view/open1384351384024.html 1. SSH简介 SSH是Secure Shell的缩写,一 ...
- 每天一个linux命令(62):sh命令 /Linux中执行shell脚本的4种方法总结
bash shell 脚本的方法有多种,现在作个小结.假设我们编写好的shell脚本的文件名为hello.sh,文件位置在/data/shell目录中并已有执行权限. 方法一:切换到shell脚本所在 ...
- SQL Server 中执行Shell脚本计算本地文件的内容大小
SQL Server 数据库中除了能执行基本的SQL语句外,也可以执行Shell脚本.默认安装后,SQL中的Shell脚本的功能是关闭的,需要手动打开, 执行以下脚本即可打开该功能. -- 允许配置高 ...
- 利用python执行shell脚本 并动态传参 及subprocess基本使用
最近工作需求中 有遇到这个情况 在web端获取配置文件内容 及 往shell 脚本中动态传入参数 执行shell脚本这个有多种方法 最后还是选择了subprocess这个python标准库 su ...
随机推荐
- 《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(下)
第 8 章 认证和安全 8.3 HTTPS HTTP 协议能够在客户端和服务器之间传递信息,特点是以明文的方式发送内容,并不提供任何方式的数据加密 为了解决 HTTP 协议这一缺陷,需要使用另一种协议 ...
- ABC 304
T4 在一个平面上有一块面积无限的蛋糕,给出 \(n\) 颗草莓的所在位置和 \(a\,(b)\) 条平行与 \(x\,(y)\) 轴的切刀位置. 切刀会把蛋糕沿 \(x\,(y)\) 轴切开.因此一 ...
- CF1295
A 用计算器式显示数字,可以显示 \(n\) 段.可以显示的最大数字是多少? 如果用了一个需要至少四段的数字,一定不如把这个替换成两个 \(1\) 好. 如果一共可以用偶数个,一定是全部 \(1\). ...
- JS Leetcode 278. 第一个错误的版本 题解分析
壹 ❀ 引 本题来自LeetCode的278. 第一个错误的版本,难度简单,端午节就应该做点容易的题目缓解心情,题目描述如下: 你是产品经理,目前正在带领一个团队开发新的产品.不幸的是,你的产品的最新 ...
- JS leetcode 最长公共前缀 题解分析
壹 ❀ 引 今天做的又是一道让我沮丧的题,思路有,但是代码逻辑最后还是没能正确理出来,题名为最长公共前缀,题目如下: 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 ...
- NC223888 红色和紫色.md
题目链接 题目 题目描述 漫长的生命总是无聊的.这天,小红和紫准备玩一个染色游戏. 她们拿出了一个有 \(n*m\) 个格子的网格,每个格子只能被染成红色或紫色.每个人可以任意选择一个格子染成红色和紫 ...
- NVME(学习笔记七)—Atomicity Operation
5.21.1.10 Write Atomicity Normal 这个特性控制AWUN和NAWUN参数的操作.设置的属性值在set Feature命令的Dword 11中表明. 如果提交Get Fea ...
- Java编程思想第四版(完整中文高清版)pdf
这是一本有独到.深入的见解的Java语言,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概念.本书共22章,包括操作符.控制执行流程.访问权限控制.复用类.多态.接口.通过异常处理错误.字符串.泛 ...
- 【Unity3D】Unity3D技术栈
1 前言 本文梳理了笔者在学习 Unity3D 的过程中,对 Unity3D 的理解和学习路线,以帮助读者循序渐进地学习 Unity3D,后续笔者仍会持续更新 Unity3D 相关技术栈,并同步到 ...
- 使用TensorFlow实现MNIST数据集分类
1 MNIST数据集 MNIST数据集由70000张28x28像素的黑白图片组成,每一张图片都写有0~9中的一个数字,每个像素点的灰度值在0 ~ 255(0是黑色,255是白色)之间. MINST数据 ...