@aliou/sh (韩语)
外壳解析器并输入了TypeScript的AST. 处理POSIX, Bash, mksh, 和zsh语法. 0运行时依赖,~66 KB捆绑.
安装
npm install @aliou/sh
需要节点.js 22+.
快开始
import { parse } from "@aliou/sh";
const { ast } = parse('echo "hello $USER" | grep hello');
// ast.type === "Program"
// ast.body[0].command.type === "Pipeline"
parse 返回 a Program 他的身体拥有 Statement 节点。 每个语句都包裹着一个 Command数字 :
| 命令 | Shell 语法 |
|---|---|
SimpleCommand | 单词、任务、重定向 |
Pipeline | a | b |
Logical | a && b, (中文(简体) ), a || b |
IfClause, (中文(简体) ), WhileClause, (中文(简体) ), ForClause, (中文(简体) ), SelectClause, (中文(简体) ), CaseClause | 控制流量 |
FunctionDecl | foo() {}, (中文(简体) ), function foo {} |
Subshell, (中文(简体) ), Block | ( ... ), (中文(简体) ), { ... } |
TestClause, (中文(简体) ), ArithCmd | [[ ... ]], (中文(简体) ), (( ... )) |
CoprocClause, (中文(简体) ), TimeClause | coproc, (中文(简体) ), time |
DeclClause | declare, (中文(简体) ), local, (中文(简体) ), export, (中文(简体) ), readonly, (中文(简体) ), typeset, (中文(简体) ), nameref |
LetClause | let i++ j=2 |
CStyleLoop | for (( ; ; )) |
单词持有打入的部分 : Literal, (中文(简体) ), SglQuoted, (中文(简体) ), DblQuoted, (中文(简体) ), ParamExp, (中文(简体) ), CmdSubst, (中文(简体) ), ArithExp, (中文(简体) ), ProcSubst, (中文(简体) ), BraceExp, (中文(简体) ), ExtGlob。每个节点携带 pos 财务报告和审定财务报表 end (单位:千美元)offset, (中文(简体) ), line, (中文(简体) ), col)指入出处.
示例:旗下危险命令
每个节点 type 字段缩小了结合,所以一般行走者可以捕捉到任何出现的特定的指令——管道, && 链、子壳、命令替换:
import { parse, type SimpleCommand } from "@aliou/sh";
function findRmRf(node: unknown): string[] {
if (!node || typeof node !== "object") return [];
const cmd = node as SimpleCommand;
if (cmd.type === "SimpleCommand") {
const [name, ...args] = (cmd.words ?? [])
.map((w) => (w.parts[0].type === "Literal" ? w.parts[0].value : ""))
.filter(Boolean);
if (name === "rm" && args.some((a) => a.startsWith("-") && a.includes("r") && a.includes("f"))) {
return [`rm ${args.join(" ")}`];
}
}
return Object.values(cmd).flatMap((v) =>
Array.isArray(v) ? v.flatMap(findRmRf) : findRmRf(v),
);
}
const { ast } = parse("deploy.sh && rm -rf ./scratch; cp -r dist/ b/ || (rm -rf b/ && exit 1)");
findRmRf(ast); // ["rm -rf ./scratch", "rm -rf b/"]
这种检查是项目的主要使用案例:检查或重写命令,然后将脚本交给执行它的东西.
选项
interface ParseOptions {
dialect?: "posix" | "bash" | "mksh" | "zsh"; // default: "bash"
keepComments?: boolean; // default: false
recoverErrors?: boolean; // default: false
}
设定 recoverErrors: true 获取部分 AST + a errors 数组而不是被丢弃的例外 。 用于分析不完整或已断开的脚本(编辑器,linters).
懒惰分析
对于大型脚本,流顶级节点不构建整个AST:
import { parseStmtsSeq, parseWordsSeq } from "@aliou/sh";
for (const stmt of parseStmtsSeq(source)) { /* one statement at a time */ }
for (const word of parseWordsSeq(source)) { /* one word at a time */ }
其他出口
splitBraces(word)扩大{a,b}编号 :{1..5}支撑扩张成一个字。 解析器默认不会释放括号扩展 .NO_POS——你手建的节点的哨站位置.- 每个AST节点的完整类型脚本.
支持的语法
- 简单的指令,管道,
&&, (中文(简体) ),||页:1&),分号 - 引证:
'...', (中文(简体) ),"...",反弹续作 - 参数扩展:
$var, (中文(简体) ),${var:-default}, (中文(简体) ),${var/pat/repl}切片,间接 - 命令替换 :
$(cmd), (中文(简体) ),`cmd` - 算术:
$((expr)), (中文(简体) ),((expr)), C 样式for (( ; ; )) - 进程替换 :
<(cmd), (中文(简体) ),>(cmd) - 此处文件( E)
<<, (中文(简体) ),<<-)和花边(<<<(中文(简体) ) - 重定向 :
>, (中文(简体) ),>>, (中文(简体) ),<, (中文(简体) ),>&, (中文(简体) ),<&, (中文(简体) ),<>, (中文(简体) ),>|, (中文(简体) ),&>, (中文(简体) ),&>>附加时{varname}fd 重定向( Bash/zsh) - 任务和附件 :
FOO=bar, (中文(简体) ),FOO+=bar,命令范围 - 矩阵 :
arr=(a b c), (中文(简体) ),arr=([0]=x [1]=y) - 控制流量 :
if/elif/else/fi, (中文(简体) ),while/until, (中文(简体) ),for/in, (中文(简体) ),select/in, (中文(简体) ),case/esac - 功能,子壳,块,
[[ ]], (中文(简体) ),(( )), (中文(简体) ),coproc, (中文(简体) ),time, (中文(简体) ),! - 扩展缩略语
@(foo), (中文(简体) ),*(bar)(巴什/克什语) - 评论(与
keepComments: true(中文(简体) )
状态
工作正在进行中。 覆盖基于 AST 的命令分析所需的 Bash 子集(分类,可变- mutation tracking, guardrail reference). 它不是一个完整的POSIX/Bash parser;不太常见的方言特征中的边框可能缺失.
发展
要求数 无 对于 dev 外壳:
nix develop
pnpm install
pnpm test # vitest
pnpm typecheck # tsc --noEmit
pnpm lint # biome
pnpm build # rolldown + declarations
许可证
麻省理工学院——见 自由主义。此工程是 TypeScript 移植 mvdan/sh (英语) (BSD 3-Clause,丹尼尔·马蒂); 原始许可证包含在 自由主义。 。 。 .