# aiagent.sh — 100% POSIX-sh 自然语言运维 Agent > 一行英文句子 = 一条运维命令。不用背参数,不用记 flag。 `aiagent.sh` 是一个**纯 `/bin/sh` 实现**的轻量级"AI Agent"。它不需要 Python、不需要 node、不需要任何运行时——单文件、零依赖,却能把你输入的**自然英语句子**解析成真实的运维操作。 ```sh $ ./aiagent.sh aiagent> please show disk usage [INFO] Disk usage: Filesystem Size Used Avail Use% Mounted on /dev/sda1 100G 42G 58G 42% / aiagent> what is the uptime [INFO] System uptime: 14:32:11 up 12 days, 3:17, 2 users, load average: 0.12, 0.08, 0.01 aiagent> check port 8080 [INFO] Checking port 8080 ... [OK] Port 8080 is IN USE ``` --- ## ✨ 核心特性 | 特性 | 说明 | |------|------| | 🗣️ **自然语言输入** | 接受完整英文句子,不需要任何特殊语法 | | 🧠 **意图识别** | 自动提取动词、名词、路径、端口、服务名 | | 🔄 **同义词归一** | `show/display/list` → 统一处理,`kill/stop/terminate` → 统一处理 | | 🎯 **名词推断动作** | 只说 `uptime` 也能理解成"show uptime" | | 🔒 **安全确认** | 所有破坏性操作(删除、杀进程、装包)强制 `y/N` 二次确认 | | 🛡️ **零 eval 注入** | 没有任何字符串拼接执行,包管理器直接调用 | | ⚙️ **自动适配环境** | 自动检测 `systemctl`/`service`、`ss`/`netstat`、`apt`/`dnf`/`yum`/`apk`/`pacman` | | 🧩 **可扩展** | 每个 `do_xxx()` 函数独立,新增操作只需加一个 case 分支和一个函数 | | 📦 **单文件分发** | 约 1050 行,直接 `cp` 到目标机就能跑 | --- ## 🚀 快速上手 ### 1. 获取脚本 ```bash # 从git仓库获取 git clone https://github.com/CancerXiaoAi/shell-ai.git git clone https://git.cancerai.top/cancerai/shell-ai.git ``` ### 2. 两种使用方式 ```bash # 方式 A:交互式 REPL(推荐日常使用) ./aiagent.sh # 方式 B:一行命令模式(适合脚本/CI) ./aiagent.sh "please check port 443" ./aiagent.sh "what is the kernel version" ./aiagent.sh "health check" ``` ### 3. 最简示例 ```bash # 系统一句话 ./aiagent.sh uptime # 直接说名词就行 ./aiagent.sh disk ./aiagent.sh health # 完整句子也没问题 ./aiagent.sh "please help me show memory usage" ./aiagent.sh "could you ping google.com for me" ./aiagent.sh "i want to restart nginx" ``` --- ## 📖 完整命令参考 ### 💡 语法约定 脚本对输入的容忍度极高。以下写法**全部等价**: ``` please help me check disk usage help me check disk check disk disk ``` 规则: - `please`、`help me`、`to`、`a`、`the`、`can you`、`would you` 等纯语法词**全部自动忽略** - 只带名词(如 `uptime`)会被自动推断为"show + 该名词" - 只带动词加短语(如 `check port 80`)直接路由 ### 🖥️ 系统信息 — System Info | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `show disk usage` / `disk` | `df -h` | 磁盘使用情况 | | `check memory` / `memory` / `ram` | `/proc/meminfo` 或 `free -h` | 内存占用 | | `check cpu load` / `cpu` / `load` | `/proc/loadavg` | CPU 负载 | | `what is the uptime` / `uptime` | `uptime` | 系统运行时长 | | `show kernel version` / `version` | `/etc/os-release` + `uname` | OS 和内核版本 | | `show listening ports` | `ss -tulnp` 或 `netstat -tulnp` | 监听端口 | | `who is logged in` / `show users` | `who` | 当前登录用户 | | `health check` / `check` | 一键跑 uptime + CPU + 内存 + 磁盘 | 健康检查 | ### 📂 文件操作 — Files | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `list files in /var/log` | `ls -lah /var/log` | 列出目录 | | `show me content of /etc/hosts` | `ls /etc/hosts` | 查看文件 | | `find files named *.log in /tmp` | `find /tmp -type f -name "*.log"` | 查找文件 | | `find process nginx` | `ps aux | grep nginx` | 按名字找进程 | | `create directory /data` | `mkdir -p /data` | 创建目录 | | `create file /tmp/hello.txt` | `: > /tmp/hello.txt` | 创建空文件 | | `copy /etc/hosts /tmp/hosts.bak` | `cp -r/-f` | 自动判断文件/目录复制 | | `move /tmp/a.log /var/log/` | `mv -f` | 移动/重命名 | ### ⚠️ 破坏性操作 — Destructive(强制 y/N 确认) | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `please remove /tmp/cache` | `rm -rf`(确认后) | 递归删目录 | | `delete file /tmp/bad.log` | `rm -f`(确认后) | 删除文件 | | `clean old log files in /var/log` | `find -delete`(确认后) | 清理日志 | | `kill process nginx` | `kill` + PID 列表(确认后) | 按名字杀进程 | | `kill the process on port 8080` | 先 lsof/ss 找 PID(确认后) | 按端口杀进程 | | `stop nginx` | `systemctl stop`(确认后) | 停服务 | ### 🔌 服务管理 — Services | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `check status of sshd` | `systemctl status` | 查看服务状态 | | `start docker service` | `systemctl start` | 启动服务 | | `restart nginx` | `systemctl restart` | 重启服务 | | `nginx` | 自动识别服务名 → 查状态 | 裸服务名默认查状态 | ### 🌐 网络 — Network | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `check port 443` / `port 443` | `ss`/`netstat`/`nc` | 检查端口监听状态 | | `ping google.com` | `ping -c 3 -W 2` | 连通性测试(3 包) | | `show listening ports` | `ss -tulnp` | 所有监听端口 | ### 📦 包管理 — Packages | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `please install package htop` | 自动检测 `apt-get`/`dnf`/`yum`/`apk`/`pacman` 并调用 | 安装系统包(确认后) | ### 📜 日志 — Logs | 输入示例 | 实际调用 | 说明 | |---------|---------|------| | `tail /var/log/syslog` | `tail -30` | 直接路径 | | `tail nginx log` | 自动匹配 `/var/log/nginx/access.log` | 按服务名猜路径 | | `follow docker log` | glob `/var/log/*docker*` | 模糊匹配 | ### 🎛️ 其他 | 输入 | 说明 | |------|------| | `help` | 打印帮助手册 | | `quit` / `exit` / `bye` | 退出 REPL | --- ## 🧠 自然语言解析原理 整个解析管线分为 **5 层**,每一层都只使用 POSIX sh 原语实现: ``` 用户输入:please help me check port 8080 │ ▼ ┌─────────────────────────────────┐ │ 第 1 层:strip_fillers() │ │ 丢弃纯语法词:please, help, me │ └─────────────────────────────────┘ │ 得到:check port 8080 ▼ ┌─────────────────────────────────┐ │ 第 2 层:跳过引导词 │ │ 跳过开头的 help/what/can 等客气词│ └─────────────────────────────────┘ │ 得到:check port 8080 ▼ ┌─────────────────────────────────┐ │ 第 3 层:canonical_verb() │ │ 动词→同义词归一 │ │ 名词→推断动作(health→check) │ └─────────────────────────────────┘ │ verb=check, phrase="port 8080" ▼ ┌─────────────────────────────────┐ │ 第 4 层:dispatch() 一级匹配 │ │ case "$phrase" in *port*) │ │ do_check_port; _matched=1 ;; │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 第 5 层:do_check_port() │ │ 从短语中提取 "8080" │ │ 依次尝试 ss → netstat → nc │ │ 输出 [OK]/[WARN] 状态 │ └─────────────────────────────────┘ ``` ### 动词同义词映射(canonical_verb) ``` show ← show, display, list, print, tell, what, reveal, dump check ← check, monitor, verify, inspect, diagnose find ← find, locate, search, lookup, track remove← remove, delete, erase, rm, wipe, cleanup, clean kill ← kill, stop, terminate, shutdown, halt start ← start, launch, run, begin, enable ... ``` ### 名词 → 推断动作 ``` uptime, disk, memory, cpu, version, port, process ... → 自动当作 show health, status, connection, reachability → 自动当作 check ``` ### 三级 fallback 匹配机制 每个规范动词的路由都有三级兜底: 1. **一级关键词匹配**:`*disk*` → do_show_disk,`*port*` → do_check_port 2. **二级兜底**:短语是纯数字 → 当作端口号;短语含已知服务名 → do_service_status 3. **三级兜底**:`check` 空短语 → do_health_check;其他 → 提示用户重述 --- ## 🔒 安全设计 ### 强制二次确认 以下操作**必须**输入 `y` 或 `yes` 才会继续,其他任何输入都视为取消: - 删除目录/文件(`remove`, `delete`) - 杀进程(`kill`) - 停服务(`stop service`) - 安装系统包(`install`) ``` [CONFIRM] Recursively remove directory '/tmp/cache' ? (y/N): ``` ### 零 eval 注入 整个脚本**没有任何 `eval` 调用**。包管理器的调用采用直接分支方式: ```sh if command -v apt-get >/dev/null 2>&1; then apt-get install -y -- "$_pkg" # 直接调用,参数安全引用 elif command -v dnf >/dev/null 2>&1; then dnf install -y -- "$_pkg" ... ``` ### 路径安全 `rm -rf`、`mv`、`cp` 等危险操作都使用了 `--` 分隔符,防止路径以 `-` 开头被解释为选项。 ### 信号处理 REPL 模式下按 Ctrl+C 会优雅退出,不会留下终端异常状态: ```sh trap 'echo; printf "Bye.\n"; exit 130' INT TERM ``` --- ## 🏗️ 架构总览 ``` aiagent.sh (~1050 lines, 单文件) ├── 辅助函数层 │ ├── info() / warn() / error() / success() # 带颜色的日志输出 │ └── confirm() # y/N 安全确认 │ ├── 解析层(第 1–3 层) │ ├── strip_fillers() # 丢弃纯语法词 │ ├── canonical_verb() # 同义词归一 + 名词推断 │ ├── _extract_path() # 从短语中提取路径 │ ├── _extract_svcname() # 从短语中提取服务名 │ └── _SERVICES_RE # 已知服务名正则 │ ├── 路由层(第 4 层) │ └── dispatch() # 大 case 分支,三级 fallback │ ├── 执行层(第 5 层) │ ├── do_show_disk / _memory / _cpu / _uptime / _version / ... │ ├── do_check_port / do_ping_host / do_service_status / do_health_check │ ├── do_find_process / do_find_file │ ├── do_remove_dir / _file / _log │ ├── do_kill_process / do_kill_by_port │ ├── do_service_start / _restart / _stop │ ├── do_create_dir / _file │ ├── do_copy_path / do_move_path │ ├── do_install_pkg │ └── do_tail_log │ ├── REPL 层 │ ├── run_one() # 单次命令处理管线 │ └── repl() # 交互式循环 │ └── 入口 └── main() # 分交互式/一行模式 ``` ### 依赖关系图 ``` main() ─→ repl() 或 run_one() │ ▼ dispatch() │ ┌─────────┼─────────────┬──────────┬────────────┐ ▼ ▼ ▼ ▼ ▼ show() check() find() remove() install() │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ do_show_* do_check_* do_find_* do_remove_* do_install_pkg │ ▼ confirm() ←── 所有 destructive 函数必经 ``` --- ## ⚙️ 运行要求 ### 最低要求 | 项目 | 要求 | |------|------| | Shell 解释器 | `/bin/sh`(POSIX sh,任何实现均可:dash / busybox / bash --posix) | | 操作系统 | Linux(主要目标);macOS 可读但部分功能受限(无 systemctl/ss) | | 权限 | 普通用户即可运行,执行破坏性/管理类命令时需要相应权限 | ### 可选依赖(根据操作自动探测) | 操作 | 依赖(择一) | |------|-------------| | 磁盘/内存/CPU | `df`, `/proc/meminfo` 或 `free`, `/proc/loadavg` | | 服务管理 | `systemctl` 或 `service` | | 端口检查 | `ss` / `netstat` / `nc` | | 进程查找 | `ps`, `pgrep`(可选) | | 进程/端口杀 | `pgrep`, `lsof`, `fuser`(择一) | | 网络 | `ping` | | 文件查找 | `find` | | 包安装 | `apt-get` / `dnf` / `yum` / `apk` / `pacman`(择一) | ### 跨包管理器支持 | 发行版 | 自动使用 | |--------|---------| | Debian / Ubuntu | `apt-get` | | RHEL 8+ / Fedora / CentOS 8+ | `dnf` | | RHEL 7 / CentOS 7 | `yum` | | Alpine | `apk` | | Arch / Manjaro | `pacman` | --- ## ⚡ 性能 解析管线全部使用 POSIX sh 内建实现(`case` / `for` / `if` / `read` / `printf`),**不 fork 任何外部进程**——除了 `grep -qE` 和 `awk` 在少数 fallback 分支里会被调用。整个"strip → canonical → dispatch → route"管线的纯解析开销**可以忽略不计**,实际耗时主要来自底层命令本身(如 `df -h`、`ps aux`、`systemctl` 等)。 --- ## 🧪 快速验证 在 Docker 容器里跑一下(无需在本机安装任何东西): ```bash # Debian docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh debian:latest \ sh -c 'chmod +x /aiagent.sh && /aiagent.sh "health check"' # Alpine docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh alpine:latest \ sh -c 'chmod +x /aiagent.sh && /aiagent.sh "check port 22"' # CentOS / Rocky / AlmaLinux docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh rocky:9 \ sh -c 'chmod +x /aiagent.sh && /aiagent.sh "restart sshd"' ``` --- ## 🛠️ 扩展指南 想新增一个操作(比如"备份数据库")?三个步骤: **步骤 1**:加一个规范动词映射(如果是新动词) ```sh canonical_verb() { ... backup|dump) echo "backup" ;; ... } ``` **步骤 2**:在 `dispatch()` 的 case 分支里加路由 ```sh dispatch() { ... backup) do_backup_db "$phrase" ;; ... } ``` **步骤 3**:实现 `do_backup_db()` 函数 ```sh do_backup_db() { _db="" for _tok in $1; do case "$_tok" in mysql|postgres|redis|mongodb) _db="$_tok" ;; esac done [ -z "$_db" ] && { error "No DB type. Example: 'backup mysql'"; return 1; } confirm "Backup $_db ?" || return 1 # 实际备份逻辑 case "$_db" in mysql) mysqldump ... ;; postgres) pg_dump ... ;; ... esac } ``` 完成。现在 `./aiagent.sh "please backup mysql database"` 就能工作了。 --- ## 🐛 已知限制 | 限制 | 说明 | |------|------| | 只能识别**英文** | 整个关键词库是英语 hardcode 的,不支持中文 | | 正则只支持 POSIX ERE | 不支持 PCRE(但对运维场景已足够) | | 交互式 REPL 不支持命令历史 | POSIX sh 的 `read` 没有 readline;可以通过 `rlwrap ./aiagent.sh` 获得历史 | | 端口号从短语提取用的是 `[0-9]*` | 如果句子里有多个数字,会取最后一个(对"check port 80 on server 192.168.1.1"这种会取 1) | | macOS 功能受限 | 没有 `systemctl`/`ss`/`/proc/*`,部分操作会降级或报依赖缺失 | | 包管理器自动检测 | 只是检测命令是否存在,不检查是否真的可用(例如在 chroot 里) | --- ## 📄 License MIT License. 复制、修改、商用均自由,唯一要求保留版权声明。 --- ## 🙏 致谢 - POSIX 标准委员会:没有你们就没有可移植的 shell - 所有写出漂亮运维命令的前辈:你们的 `df -h`、`ps aux`、`ss -tulnp` 是这个项目的基石