DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

SpookyWaste /

SpookyWaste/dsh-bash-native

Verified

Rust 实现的 Windows 原生 POSIX bash 执行器:给 DSH 提供一个真正原生的 bash(不依赖 WSL 或 MSYS兼容层),支持 DSH 的三档权限策略、提供持久化终端工具

★ 23 Stars0 Forks2 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@381a8ba9

🐚 dsh-bash-native

Rust 实现的 Windows 原生 POSIX bash 执行器

给 DSH 的 shell 提供一个真正原生的 bash —— rubash 引擎,不依赖 WSL,也不需要 MSYS 兼容层


适配三档权限策略 · 后台作业 · 支持持久化终端


Listed on dsh-plugin.org npm downloads npm DSH Powered by rubash

中文  ·  English


🐟 为什么需要它

避免肥鱼被 cmd 咬,也规避了子系统和 MSYS 兼容层的性能开销

方案 命令在哪里执行 每条命令的代价
Git for Windows MSYS2 兼容层上的移植 bash,POSIX 语义由运行时模拟 兼容层每次起子 shell 或外部命令都要完整 fork 一个 MSYS2 进程(挂起线程、另起进程、经管道搬运内存),开销随子 shell 启动频率线性放大
WSL 命令在 Linux VM 的发行版里执行 每条命令都要经 Windows/Linux 边界往返一次,延迟对高频短命令尤为不利
本插件
rubash 引擎 + 工具链
纯 Rust 实现的原生二进制,无模拟层、无 VM grep、sed、awk 等来自随包工具链,其余由引擎在进程内处理,没有兼容层或 VM 边界

⚡ 实测:启动与子 shell 的代价

同一台机器(Windows 11 / x64)上的同一组脚本,每项取 5 次最小值,负载只用 shell 内建与一次外部进程,排除工具链差异。

复现:仓库里的 node scripts/bench-engines.mjs。

工作负载 rubash(本插件) Git Bash / MSYS2
bash -c :(启动到退出) 7 ms 25 ms
echo hi 6 ms 26 ms
算术循环 10k 次 65 ms 75 ms
[ ] 判定 20k 次 167 ms 181 ms
命令替换 300 次 11 ms 3052 ms
子 shell 300 次 25 ms 2497 ms
外部进程 20 次(cmd /c exit) 192 ms 560 ms

差距来自 fork:MSYS2 每次命令替换与子 shell 都要 fork 一个兼容层进程(约 8–10 ms),rubash 两样都做在进程内(每次约 0.04 ms),300 次就相差 2–3 秒。

🧭 路径不会被“自动替换”

Git Bash / MSYS2 把参数交给原生程序时会猜哪些像路径并改写(/flag → C:/Program Files/Git/flag,于是脚本和 agent 被迫记着 MSYS_NO_PATHCONV=1);连数据位也一起改:用 MSYS 调原生 awk.exe 跑 awk '/x/{print}',程序位到手是 X:/{print},直接语法错误、进错路径。

rubash 不猜:按子进程方言,整条 argv 要么原样交给带 POSIX 层的程序,要么统一翻成 Win32 拼写;随包工具的脚本、模式、程序位永不改写,极大减少了找不到路径的情况。


📦 安装

CLI 安装

以 web profile 为例

dsh plugin --profile web add dsh-bash-native

dsh 网页版 / 桌面版安装

dsh 插件页面 → 右上角 添加插件 → 填入插件名 dsh-bash-native 或仓库地址:

https://github.com/SpookyWaste/dsh-bash-native

重启后,在 Settings → General 里把 Native Bash (Windows) 设为默认 preset(或在会话里切过去)。

持久化终端组件默认关闭,在插件管理中可以打开。见持久终端组件。

平台 Windows x64
DSH >=0.2.0-rc.2 <0.3.0-0(0.2.0-rc.2 - 0.2.2-rc.1实测)
持久终端组件 需要 DSH >=0.2.1-alpha.1才能开启

📝 Changelog

  • 0.3.1:引擎由 brush 更换为 rubash ,修复多项已知缺陷,提供更好的原生 POSIX 兼容
  • 0.3.3:引擎升级到 rubash 1.5.0——三笔补丁 150-001–150-003,旧补丁随上游合入退役
  • 0.3.5:更新工具链jaq和 findutils 到最新 release 。为 du 命令添加新补丁,优化多文件的统计速度(见下)。
树 旧(逐条目) 补丁(枚举)
20,000 文件 1.29 s 45 ms
228k 文件 工作区 >600 s 3.2 s

⚙️ 引擎

引擎是 rubash(unixwin/rubash,Rust 写的 bash 5.3 兼容 shell,MIT),本仓库从源码构建它、按 engine.lock.json 里的 commit 应用三笔补丁,产物 engine/win32-x64/rubash.exe(约 14 MB)随包提交。

兼容 MSYS 的 uname -s 身份(MSYS_NT-…),所以脚本里按 MSYS 分支的逻辑也能成立;并发、取消与作业语义由引擎自己实现,跟 MSYS 运行时无关。

🧩 补丁清单(点击展开)
补丁 做了什么
150-001-cd-into-virtual-roots-with-a-real-directory cd 进 /tmp 这类背后有真实目录的虚拟根时,把会话 cwd 也一起移动(1.5.0 里 cwd 是会话状态,不再是进程 cwd):原来只改 PWD 就返回,于是 pwd 报新目录、相对操作数与子进程却还在旧目录。PWD、pwd -P、DIRSTACK 仍然显示 /tmp;/usr、/etc、/bin 没有目录可去,/ 没有唯一目标,这四个名字保持只改逻辑状态。
150-002-default-a-unset-locale-family-to-utf-8-on-windows 没设 LC_ALL/LC_CTYPE/LANG 时,Windows 上默认按 UTF-8 字符语义:${#中文} 是 2、[[:alpha:]] 认 CJK、${v:0:2} 切两个字符。1.5.0 把空的 locale 族解释成 C locale(按字节),这条把它改回 1.4.3 的默认;显式写 LC_ALL=C 仍然按字节。
150-003-identity-tools-answer-without-a-path-program PATH 上没有 uname/arch 程序时仍由引擎作答(MSYS 人设):1.5.0 的进程内仿真要先解析到程序,否则让位给一次必然失败的 spawn。可解析的一侧不变——PATH 上的同名脚本仍然会运行。

💡 每次解析都按 lock 里的 sha256 校验产物,并按盖章跳过重复校验:产物的 size 与 mtime 记进 %LOCALAPPDATA%\dsh-bash-native\verified\,没变就不再读那 14 MB(verifyArtifacts: 'always' 可恢复每次都哈希)。


🔧 实现

  • 注册 ctx.shell:ShellExecutor 的 Service Provider,构建在 @deepseek-ai/dsh-bash-local 之上,每条命令交给解析到的 rubash 引擎;引擎与工具链都随包分发,并在解析时按 lock 与 manifest 校验。
  • 服从 DSH 的三档文件策略(read-only / workspace-write / danger-full-access):受限档位把引擎 argv 交给 ctx.sandbox,不限档位直接 spawn 并如实报告档位。
  • 后台作业经 ctx.jobs 汇报,超出内存上限的输出落到 spill 文件、路径随结果返回;echo、cat、sort 等空 PATH 也能跑,因为随包工具链先于宿主 PATH。
  • Web 面用自带的 agent preset 挂载,agent 面用 overlay 显式启用;它不替换宿主层的 ctx.shell,因此其他 preset 的 pwsh 工具不受影响。

🎛 两个预设

cordis.patch.yml 只有一行注册器(dsh-bash-native/presets),两个 preset 由插件在运行时注册:启动时读本机安装的 presets/{standard,minimal}.patch.yml,把 shipped 的 shell 面换成插件自己的 realm group,不影响其他 preset。

预设 挂载什么 什么时候选
Native Bash (Windows)
id bash-native,order 5
读本机 harness 那条线的 standard 并只换 shell 面:每次调用都是全新的 bash,文件、作业、委派等全套工具跟着那一版上游走 日常使用
Native Bash (Windows, minimal)
id bash-native-minimal,order 6
读本机那条线的 minimal 并只换 shell 面:一个 persona 与一个持久 bash。 只要一个 shell 的轻量会话

📟 持久终端组件

插件页面里 dsh-bash-native 提供两个可分别开关的组件:dsh-bash-native/presets(两个预设,默认开)与 dsh-bash-native/terminal(提供 6 个交互式持久终端工具,默认关)。

该组件和内置的 持久终端(@deepseek-ai/dsh-experimental-terminal-bundle)注册同名工具,不能也不需要同时开启。 (tool "terminal_open" is already registered)


🛠 配置

除下表外,执行预算继承自 @deepseek-ai/dsh-bash-local:cwd、timeoutMs、maxTimeoutMs、maxOutputBytes、maxSpillBytes、graceMs,默认值与上限由那一侧的 schema 单一出处提供。

键 默认 含义
confine true 是否服从 ctx.sandbox;关闭即放弃文件策略与拒绝标记
requireEngineOnLoad false 无引擎时是加载失败,还是每次调用失败(两个预设都设 true)
toolsDir '' POSIX 工具链目录;空 = 先看每用户目录里自建的那份(%LOCALAPPDATA%\dsh-bash-native\tools\bin),没有程序就用随包工具链。填了则照用,空目录也算数(不会悄悄换成随包那份)
rcFile '' 持久 PTY 会话读的启动文件;空 = %LOCALAPPDATA%\dsh-bash-native\bash-native-rc.sh
denialSignatureAdditions ['os error 5'] 追加的拒绝签名,默认值让非英文 Windows 也标得出拒绝
shellEnvOverrides {} 追加环境变量,层叠在模型友好默认值之上
verifyArtifacts 'stamped' 校验力度:stamped 只在产物或运行副本的 size/mtime 变化时重新哈希;always 每次解析都哈希(引擎 14 MB、工具链 37 MB)

🧰 工具链

工具链随包分发,装完即用。

📚 101 个名字,公布其中 20 个

| 类别 | 内容 | |---|---|---| | 公布(20) | grep、sed、awk、jq、find、xargs、diff、cmp、which、timeout、stat、ps,以及 tty、nohup、nice、uptime、hostid、pathchk、locate、updatedb | | 只装不公布(81) | uutils coreutils 的 88 个程序(cat、ls、rm、cp、head、tail、wc、sort、seq 等——引擎不再自带这些),以及 shell 内建要用的程序形态 echo、printf、pwd、test、true、false、kill |


  • 支持与引擎相同的 /tmp 与盘符别名,“程序再启动程序”的链同样成立:printf '/tmp/f\n' | xargs awk '{print}' 与 printf '/tmp/x\n' | xargs rm 都拿到真实路径。

  • 数据位由两边同一张表保护:开关 -e/-f 的值、grep/sed/awk 的首个位置操作数、find 的 -name/-path 类主项、basename/dirname 的位置参数,都不翻译——引擎启动的子进程与工具链里“程序再启动程序”的那一半用的是同一份清单。

  • 顺带把 bash 与 sh 放到 PATH 上(工具链目录在前,名字目录紧随其后,两者都排在宿主原有 PATH 之前):

    src/shim.ts 在 %LOCALAPPDATA%\dsh-bash-native\shim\<引擎 sha256>\ 里把这两个名字指向已校验引擎——同卷时是硬链接,两个名字共用引擎那一份字节。


⚠️ 已知限制

  • 两条与进程内副本无关的平台差异:

    形态 实测
    cd /absent 2>err.txt | cat 诊断既不在 err.txt 也不在 shell 的 stderr 上
    printf … | head -c 1 的 PIPESTATUS[0] 内建写端不报断管,0 而不是 141
  • 断管(读端提前关闭)时,工具与外部程序用它们自己的措辞和退出码——这是 Windows 没有 SIGPIPE 的后果(每个工具各自决定断管怎么办):

    命令 写端退出码 备注
    seq 1 200000 | head -c 1 0 stderr 打 write error: Broken pipe
    yes | head -c 1 0 不吭声
    cat big | head -c 1 13 —
    ls -R 某目录 | head -c 1 1 —
    引擎自己的内建 0 不报断管(见上一条 PIPESTATUS[0])

🧹 磁盘足迹与清理

运行时状态都在 %LOCALAPPDATA%\dsh-bash-native 下,并且按内容寻址:每个引擎构建一个 shim/<引擎 sha256>/。

目录 谁写的 大小 删掉会怎样
shim/<摘要>/ 每次解析 引擎大小(同卷硬链接时约 0) 下次解析重建
toolchain/<摘要>/ 每次解析 约 36 MB(同卷硬链接时约 0) 下次解析重建
verified/ 每次校验 几十 KB 只会多一次哈希

显式清理(停掉 DSH 之后运行):

将替换为你的profile路径,例如:%USERPROFILE%\.dsh\profiles\<profile>

node <dsh_profile>/node_modules/dsh-bash-native/bin/clean-state.mjs          # 只报告,不动文件
node <dsh_profile>/node_modules/dsh-bash-native/bin/clean-state.mjs --yes    # 删掉被取代的 shim/ 与 toolchain/ 缓存

📄 许可

内容 许可 声明与扫描
本插件 MIT LICENSE
rubash 引擎 MIT engine/LICENSE.rubash、依赖扫描 engine/THIRD-PARTY.md
随包工具链 MIT uutils coreutils/findutils/grep/sed、jaq、goawk、仓库内 posix-extra;toolchain/LICENSES/ 与 toolchain/THIRD-PARTY.md

🙏 参考与致谢

为 Agent 提供原生 bash 这条路上,oh-my-pi 是最早的先行实现之一,本插件早期针对 brush 的诊断与修法也受益于它留下的记录。

—/ 5

No ratings yet

Verified DSH bundle

Commit 381a8ba9c059

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout