我平时老在一台 Android 电视上调试,adb shell 是怎么也绕不开的:查应用版本号要 dumpsys package | grep,看内存得记住 /proc/meminfo 的路径。命令本身都不难,但天天敲就烦。后来我就想,能不能直接说一句"看看内存",让程序自己把命令跑了,再用大白话把结果告诉我——nl2sh 就是这么来的。
它干的事很简单:你把自然语言交给 OpenAI 兼容的模型,模型通过 Tool Calling 生成 shell 命令,在本地做风险分类、等你确认后再执行,真实输出再回传给模型,继续往下对话。
整个程序是一个用 stable Rust 写的单文件 Android 可执行文件,adb push 到 /data/local/tmp 就能跑。设备上既不需要 Termux ,也不需要 Python 运行时。
为什么不直接用 Termux 或写个 App
Termux 得在设备上装一整套环境,分发和升级都重。App 方案要走用户态拿 shell 或 root ,路径更绕。
nl2sh 选了交叉编译这条路:NDK r28c 、API 26 起步,出 aarch64 和 armv7 两个 ABI 的产物,Android 8 以上的主流设备都能覆盖。
执行链路和安全模型
在我的测试机( API 34 的 Android 电视)上,执行链固定四步:模型生成命令 → 本地分类 → 用户确认 → 执行。
风险分四级:只读、修改、危险、严重。默认策略下:
- 只读命令:自动执行
- 修改命令:弹一次确认
- 危险命令:二次确认
内置检测覆盖了 rm -rf、格式化、块设备写入、递归改根目录权限、重启关机、分区擦除、remount 等。自定义规则只能往高风险调,不能松绑内置检查。
有几条硬规矩:
- 模型决定不了确认流程、风险等级和 root 提升
- 用户编辑过的命令必须重新分类
- root 提权走参数化的
su -c 传参,不拼 shell 字符串,避免注入
- 没有 TTY 时,修改和危险命令直接拒绝——用管道伪造确认这条路也堵上了
终端这块踩了不少坑
Android 的终端环境比桌面乱得多。比如 adb 会话会把鼠标 SGR 序列拆成按键字符,不过滤就有一串乱码混进输入框。
命令执行走 openpty ,实时输出;超时就清理整个进程组,不留僵尸进程。碰到 vi、top 这类全屏交互程序,临时切到本地 raw mode 桥接键盘,同步窗口尺寸,退出后恢复 TUI 。
界面用 ratatui + crossterm 写的,中英文都支持,默认中文。Agent 的回复在终端里直接渲染 Markdown ,表格按中英文宽度对齐,窄屏自动降级。命令输出默认折叠,按 F2 展开; Ctrl+C 取消当前任务,Ctrl+Q 退出。
怎么跑起来
不用装 Rust ,也不用装 NDK 。
GitHub Release 里按设备 ABI 下载压缩包:
- Linux:
./android-run-linux.sh
- Windows:PowerShell 里跑
./android-run-windows.ps1
脚本会自动 push 并启动,Windows 那边不用装 WSL 。
首次启动用方向键选服务商,OpenAI 、DeepSeek 、Kimi 、SiliconFlow 、Ollama 或者自定义地址都行,填完 API Key 直接进界面,配置写进 0600 权限的文件。连本地 Ollama 的话 Key 留空就行。
现状
53 个测试全过,cargo fmt 和 clippy -D warnings 都干净。真机上验证过 Agent 多轮对话、PTY 执行和 TUI 终端恢复。
root 提权、修改确认、超时和全屏交互程序这几个场景还没充分验证,手头有设备的同学欢迎一起试。项目还在早期,报 bug 或者提想法都行。
仓库在 github.com/Ernest-su/nl2sh ,Release 页面有 v0.1.0 的预编译包。
写出来动机很朴素:电视调试我天天干,能少敲几条命令就少敲几条。