CLI-Anything:统一命令行任务入口,告别脚本散乱与记忆负担
前阵子整理工作流的时候我发现自己电脑上散落着三十多个脚本和十几个命令别名有的是Python写的有的是Shell还有几个是以前用Node临时凑的。每次想跑某个任务都得先回忆这个脚本当时放在哪个目录参数是怎么传的环境变量有没有漏掉。更麻烦的是团队里的同事想复用这些工具根本不知道它们的存在每次都得靠我人肉口述操作方式——这不是效率工具该有的形态。后来我索性花了两个周末做了一件事把所有重复性的命令行操作统一收口进一个工具取名叫CLI-Anything宗旨就一句话——“任何重复的事情都应该有一个CLI入口”。这篇文章不聊空泛的概念直接把这个工具的设计思路、核心实现、全套实操配置和踩过的坑一次讲清楚。如果你也在为“脚本太多、命令太乱、知识只留在自己脑子里”这件事发愁那么这篇文章应该能给你一套立即可用的方案。1. 为什么需要“CLI-Anything”这类工具1.1 脚本越积越多真正的痛点在哪里每个人都会经历这样一个阶段一开始只是偶尔需要一个工具于是写了一条命令过了几天发现这条命令太长就存成了别名又过了一阵子同样的逻辑要在不同目录反复用于是把它抽成一个脚本文件加参数。从单条命令到几十个脚本这个过程几乎是自然生长的没有任何规划。真正的问题不是“脚本多”而是“入口散”。你记得脚本写在哪个目录但电脑重启之后你未必记得那个目录在哪你记得参数顺序但三个月后的自己完全不记得。于是出现了一种很常见的现象每次用到这些工具必须先翻历史记录或者翻Git仓库才能找回用法本应该节省时间的自动化反而变成了一种记忆负担。CLI-Anything 的核心定位就在这儿它不是一个脚本框架不是编程语言也不是任务调度器而是一个“任务注册表 统一执行入口”。你只需要把命令、参数、说明写进一份配置文件之后所有任务都在同一个界面下管理和运行。相当于给你所有散落的脚本装了一个统一的“抽屉”再贴上标签。1.2 它到底解决什么问题要理解这个工具的价值先看一组日常场景每周五要备份一次数据库而且要按日期归档你每次都要手动敲一串mysqldump加时间戳、指定目录。公司有5个项目目录你想一次性看所有仓库的 Git 状态得写个循环或者一个一个目录进去看。给博客配图每次都要把手机拍的照片压缩到指定尺寸、重命名为固定格式再用工具传到服务器。发布新版本时要依次执行跑测试、构建、上传服务器、清理旧版本缓存。每步之间如果出错就得手动停下来处理。这些任务没有一个是复杂的但它们共同的问题是重复、稳定、步骤固化。每次执行的内容完全一样只是某些参数时间、文件名、目录不同。这正是应该被统一管理的工作类型。CLI-Anything 把所有这类任务收进一个配置文件通过anything run 任务名来调用参数通过统一的机制传入省去反复记忆命令的负担。1.3 适合谁用不适合谁用我自己的使用感受是CLI-Anything 特别适合这三类人日常工作离不开终端的开发者、运维、数据分析师他们有很多高频命令需要沉淀。一个团队里负责维护“公共工具集”的人与其让大家各自记一套操作手册不如直接维护一个共享的任务配置仓库。重度自动化爱好者哪怕用电脑只做一些日常事务批量改文件名、整理下载目录也值得试试。这个工具不适合的人是只做一次性操作、用完即走的人。为一次性任务写配置完全是在浪费时间。CLI-Anything 的价值模型建立在“任务的重复次数”上——同一个任务被执行得越频繁在这上面做配置的回报率就越高。2. 架构设计为什么是“配置驱动”而不是“脚本驱动”2.1 先明确一个边界这不是一个脚本框架很多人第一次看到 CLI-Anything 的项目结构会误以为这又是一个“用YAML写脚本逻辑”的工具。这个理解不对而且很危险。如果你打算在配置文件里写一堆判断和循环那说明你对这个工具的定位理解偏了。CLI-Anything 只做三件事收集信息从配置文件中获取任务列表、参数定义、描述信息。处理参数把用户输入的命令行参数解析、校验、拼装成最终要执行的命令。执行命令在指定的工作目录、环境变量下执行用户配置的命令或脚本并把输出展示出来。换句话说真正的业务逻辑应该写在 Shell 脚本、Python 脚本或其他任何你熟悉的工具里。CLI-Anything 在中间扮演“接线员”这个角色。为什么要刻意做这个减法因为一旦配置驱动架构去承担业务逻辑它很快就会变得像一个脚本语言但又没有脚本语言本身的表达能力。到时候你会发现自己一边在YAML里写糟糕的循环一边还要学习一套全新的语法成本远大于收益。保持工具简单是长期可维护性的关键。这条设计原则适用于几乎所有自动化工具。2.2 配置驱动对比脚本调用的优势其实你也可以直接建一个文件夹把所有脚本放进去然后写一堆alias来调用效果差不多。那为什么还要用一个配置驱动的工具主要区别在于以下四点描述性信息与逻辑分离在alias这种方案里任务背后是什么、有什么参数、该在哪个目录跑全靠人脑记忆。而配置驱动工具要求每一条任务都有description描述、params参数定义、cwd工作目录这些元数据。这些信息对机器不重要但对人类非常关键——你只要运行anything list所有可用任务一目了然。参数校验与错误提示命令行的核心难点是参数处理。$1、$2这种位置参数很容易传错。配置驱动的架构可以定义每个参数是否必填、默认值是什么、是否需要提示用户输入。用户传错了能直接给好看的错误提示而不是等脚本运行到一半才报错。统一的交互体验每个脚本自己实现一套--help和用户交互质量参差不齐。统一入口之后无论任务背后是什么语言实现的用户的交互方式都是anything run 任务名 --参数 值。降低了学习成本。便于沉淀和共享配置文件是一个纯文本可以放进Git仓库。团队里任何人拉下来就是一份完整的“团队工具手册”。2.3 实现语言的选择Python 为何更适合这个工具的实现参考了社区里几个同类项目的经验最终推荐用 Python 作为主语言。原因很简单跨平台、生态好、写起来快。Python 的argparse或click、typer这类库已经把所有命令行解析的复杂逻辑解决了你只需要关注任务编排本身。另外 Python 的 YAML 配置解析生态非常成熟PyYAML进程管理可以用subprocess标准库。相比之下如果选 Go编译分发是方便但迭代速度慢选 NodeJSON/YAML 处理方便但作为命令行工具分发时需要额外处理运行时依赖。类 Unix 用户macOS、Linux和 Windows 用户配合 Git Bash 或 PowerShell都能跑这比纯 Bash 脚本方案要通用得多。3. 核心细节解析配置结构的每一项都有讲究3.1 配置文件整体结构CLI-Anything 的配置文件默认放在~/.config/cli-anything/tasks.yamlmacOS/Linux或%USERPROFILE%\.config\cli-anything\tasks.yamlWindows。你也可以通过环境变量CLI_ANYTHING_CONFIG指定其他路径。一份最小化的配置长这样version: 1.0 defaults: shell: /bin/bash # 默认解释器 timeout: 300 # 默认超时时间秒 tasks: backup-db: description: 备份 MySQL 数据库到带时间戳的目录 command: mysqldump -u {user} -p{password} {db_name} {backup_dir}/{db_name}-{timestamp}.sql params: user: alias: -u default: root password: secret: true required: true db_name: required: true prompt: 请输入要备份的数据库名 cwd: ~/backups env: BACKUP_BASE: /data/backups这个结构里的每一项都不是随便设计的。下面逐项拆开讲。3.2 任务命令与步骤列表单命令还是多步骤command字段处理单个命令。它在执行时会先经过参数替换把{user}、{db_name}这类占位符替换成用户传入的值然后把整个字符串交给配置的 shell 去执行。但很多任务不是一条命令能搞定的比如发布流程先测试再构建再上传。如果你把这些都塞进一行命令里面用连接可读性会变得很差而且中间任何一步失败了你很难定位是哪个环节出的问题。所以 CLI-Anything 还支持steps列表格式tasks: publish: description: 发布到生产环境 steps: - name: 运行测试 command: npm run test - name: 构建生产包 command: npm run build env: NODE_ENV: production - name: 上传服务器 command: rsync -avz --delete dist/ deployserver:/var/www/html/ timeout: 600用steps的好处有三个每一步可以有独立超时时间每一步可以配置不同的环境变量执行过程中每一步的名称会显示在终端里出问题能一眼定位。执行策略默认是“失败即停止”——任何一步返回非零退出码后续步骤不再执行。这也是发布类任务最合理的默认行为。3.3 参数传递的三种方式各自适用什么场景CLI-Anything 的参数系统设计讲究“用最简单的方式覆盖大多数场景”。支持三种参数来源命令行参数anything run backup-db --db_name mydb。最常用适合在脚本 / 终端里直接调用。默认值兜底配置参数时设置default用户没传时用默认值。适合那些“90%时间用默认值”的参数比如备份目录、超时时间。交互式提示配置参数时加上prompt字段如果用户没传这个参数执行时工具会交互式询问。适合密码、数据库名这种不能硬编码的参数。这三种方式是可以组合的。优先走命令行参数没传就检查默认值最后才弹交互提示。这套逻辑保证了你在自动化脚本里调用时不会卡在交互上手动运行时又不会被参数名折磨。有一个细节值得特意点出来secret: true参数不会回显在日志中也不会存进执行历史。密码这类信息如果直接写在command里会随着日志打印出来存在安全隐患。加上secret标记后参数值在日志里会被替换为***只在子进程环境变量中完整传递。3.4 环境变量与工作目录别让任务偷偷跑在错误的上下文里命令行工具常见的坑就是“在不同目录下执行效果不同”。脚本依赖相对路径你从不同目录调用它就得到不同结果而且很难排查。CLI-Anything 通过cwd字段强制指定执行时的工作目录从根本上杜绝了这个问题。环境变量的处理用的是env字段。它的值并不只覆盖正在执行的命令模板字符串同样可以引用环境变量。比如tasks: pack-assets: command: python scripts/pack.py --output {ASSET_OUTPUT} env: ASSET_OUTPUT: ~/assets/output这样既保持了配置的灵活性又避免了在字符串里重复硬编码路径。3.5 为什么特意不内置“变量计算”功能你在社区里可能见过某同类工具引入了$((expr))这类模板引擎能力甚至能在配置里做字符串拼接、日期计算。CLI-Anything 刻意没有做这些。原因是如果配置文件里需要计算逻辑说明任务本身的复杂度已经超出了配置层的承载范围这时候更好的做法是把这个逻辑写成一个真正的脚本然后在 CLI-Anything 里调用。配置文件应该像一张菜单而不是一个厨师的配方。菜单只需要告诉你“这个菜是什么、有什么忌口、上菜需要多久”至于菜怎么做那是后厨的事。4. 实操从零搭建一套自己的 CLI-Anything4.1 安装与初始化安装采用最小化方案安装完就是一个命令。如果你是 Python 环境直接pip install cli-anything如果你用的是 Node 环境也可以从 npm 安装npm install -g cli-anything两个版本的核心机制完全一致。装完先验证一下anything --version如果提示找不到命令通常是安装的 bin 目录没有加入PATH。Python 用户运行python -m site --user-base查看路径把它下面的 bin 目录加进去或者直接用pipx来安装可以省掉这些环境变量麻烦。初始化配置anything init这会在~/.config/cli-anything/下生成一个示例tasks.yaml自带两三个演示任务。跑一下anything list你会看到示例任务和它们的中文描述anything list示例配置本来就是为了让你快速上手写的可以先改着玩之后再清空替换成自己的内容。4.2 第一个实战任务备份数据库我拿我自己最常用的“数据库备份”任务来演示全过程。先在配置文件的tasks:下面加一段backup-db: description: 备份 MySQL 数据库到时间戳目录 command: mysqldump -u {user} -p{password} {db} | gzip {backup_dir}/{db}-{timestamp}.sql.gz params: user: alias: -u default: root password: secret: true required: true prompt: 请输入 MySQL 密码 db: required: true prompt: 请输入要备份的数据库名 backup_dir: default: ~/backups env: timestamp: 这里有个需要额外说明的设计{timestamp}怎么来的CLI-Anything 内置了一个简单的“执行时变量”目前提供{timestamp}当前时间戳、{date}当前日期和{pid}当前进程ID使用时会自动替换成实际值。这是一个刻意控制的特性因为“时间戳加文件名”这类需求实在太普遍了内置比让用户写命令替换更省事。然后运行anything run backup-db -u admin --db shop_db按提示输入密码后最终执行的就是下面这条命令mysqldump -u admin -p{实际密码} shop_db | gzip ~/backups/shop_db-20250616123045.sql.gz执行完终端会显示任务耗时、输出文件路径和退出码。整个过程一气呵成。提示{password}虽在配置里但secret: true可以避免参数值打印到日志。不过mysqldump的-p方式本身还是会让密码潜在出现在进程列表中如果你追求更高安全性应该改用MYSQL_PWD环境变量来传密码。4.3 用“参数别名”兼容多年习惯用alias字段可以给参数起短选项名。这个设计是为了兼容已有操作习惯。像我以前习惯用-u而不是--user配置alias后两种输入方式都可以被正确解析anything run backup-db --user admin --db shop_db anything run backup-db -u admin --db shop_db不影响原有习惯还统一了新入口。4.4 查看任务详情与试运行新任务越来越多之后难免会忘记某个任务的参数细节。这时不需要打开配置文件anything info backup-db它会展示任务的完整描述、参数列表、默认值和实际要执行的命令模板。还有一个我强烈建议每个人都用的功能--dry-run。它会把“将要执行的命令”完整打印出来但不会真正执行。发布类、删除类、备份类的危险任务建议都先跑一遍 dry-run 看脚本拼接是否正确再正式执行anything run publish --dry-run5. 高频实战样例直接抄的配置5.1 批量重命名文件设计师或内容创作者经常会遇到“把一批图片改成统一命名格式”的场景。用 CLI-Anything 配置之后一行命令完成rename-images: description: 批量重命名图片文件加上日期前缀 command: | cd {dir} for f in *.{ext}; do mv $f {prefix}-$(date %Y%m%d)-${f} done params: dir: required: true ext: default: jpg prefix: default: photo这里的command字段用了|块状语法多行 shell 脚本可以直接写进配置里。注意我在第一条cd {dir}之后才做操作head 的基本原理就是“先进入目标上下文再操作”避免目录不对导致的问题。执行方式anything run rename-images --dir ~/Pictures/holiday --ext png --prefix beach5.2 一分钟查看所有 Git 仓库状态如果你像我一样同时维护多个项目逐个目录执行git status太浪费时间。配置一个多仓库检查任务git-all: description: 遍历 projects 目录下所有 Git 仓库显示当前状态 command: | for repo in {projects_dir}/*; do if [ -d $repo/.git ]; then echo $(basename $repo) git -C $repo status --short echo fi done params: projects_dir: default: ~/projects说实话这不是 CLI-Anything 独有的能力但在它有“统一入口”之后团队里任何人想快速看全量仓库状态不用问“你那个脚本叫什么名字”直接anything run git-all就行。这就是统一入口的价值。5.3 一键发布静态博客个人博客发布往往要经过构建、压缩、上传几个阶段配置成多步骤任务非常合适deploy-blog: description: 构建并发布静态博客 steps: - name: 清理缓存 command: rm -rf public .cache - name: 构建站点 command: hugo --minify env: HUGO_ENV: production - name: 压缩资源 command: tar -czf dist.tar.gz public/ - name: 上传服务器 command: scp dist.tar.gz userblog-server:/tmp/ - name: 远程解压部署 command: ssh userblog-server tar -xzf /tmp/dist.tar.gz -C /var/www/html --strip-components1 - name: 清理临时文件 command: rm -f dist.tar.gz每一步都有名称执行时终端会像流水线一样把过程打出来。如果第四步上传失败不用看晦涩的报错直接知道是“上传服务器”这一步出问题了。5.4 临时HTTP服务有时候只是想在某个目录下起一个静态文件服务但总是记不住端口和命令。加一条serve: description: 在当前目录启动 HTTP 静态服务 command: python3 -m http.server {port} --bind 127.0.0.1 params: port: default: 8080 dir: default: . cwd: {dir}嵌入默认值工作目录指定的方式五分钟就多一条顺手任务。6. 常见问题与排查技巧6.1 问题速查表现象原因解决办法提示anything: command not foundbin目录未加入PATH用pipx重装或手动把 Python bin 目录加入 PATH执行任务时中文乱码系统默认编码不是UTF-8在defaults.env里设置PYTHONIOENCODING: utf-8Windows 下可加chcp 65001参数值含空格导致命令断裂参数未加引号CLI-Anything 默认会用安全引号包装参数值确保值中含空格时依然完整任务在默认 shell 里报语法错误系统 shell 不是 Bash在任务里指定shell: /bin/bash或调整默认 shell输出大量日志找不到有效信息日志混在一起用--verbose查看拼接出的完整命令定位替换是否正确密码出现在日志中未标记为secret把相关参数加上secret: true任务超时仍然后台运行超时后子进程未被杀掉检查版本是否支持进程组清理升级到最新版并发执行同一任务时数据错乱多进程同时操作同一临时目录在命令里用{pid}区分临时文件名6.2 踩坑实录我修复过的三个低级错误第一个坑是关于参数拼接的。我早期配置过一个“上传到服务器”的任务命令写在双引号字符串里参数值是服务器路径。有一个服务器路径里带空格每次执行都只传了一半路径报错五花八门。后来解决办法是给所有参数值做了安全包裹——在替换模板变量时如果值中包含空格或特殊字符自动加单引号包裹。自那之后这类问题就很少出现了。第二个坑是环境变量覆盖。我曾在任务env里设了一个LANGzh_CN.UTF-8结果某个脚本的输出格式全部变了和我在终端手动跑的结果完全不一致。原因是我覆盖了LANG而脚本内部依赖它做 locale 判断。现在的经验是如果任务本身对 locale 敏感不要在env里特意设LANG继承当前终端的设置通常更靠谱。第三个坑是超时处理。早期版本超时之后只是通知用户子进程还挂在后台导致端口被占用、文件锁没释放。后来改成超时后直接把整个进程组杀掉并在配置里支持按步骤设置超时时间。备份大数据库或上传大文件的步骤要单独调大超时时间。6.3 高频误区别在配置文件里写复杂逻辑很多刚接触 CLI-Anything 的人会犯同一个错想用 YAML 模板的字符串拼接功能去做复杂日期计算或者在command里塞一大段 bash 逻辑。这些尝试本质上是把配置文件当成了编程语言在用。我在 2.1 节已经强调过这个边界这里再给一次建议当你的command超过 20 行的时候就该把这段逻辑抽成一个独立的脚本文件然后让 CLI-Anything 调它。毕竟配置文件的第一要求是可读性第二要求还是可读性第三——第四才是执行力。7. 团队协作与进阶玩法7.1 把配置文件当作团队公共资产CLI-Anything 的单个配置文件和代码库一样可以纳管进 Git。团队可以建立.cli-anything/tasks.yaml仓库大家在同一个中心仓库里维护任务。协作模式推荐这样设计仓库根目录放tasks.yaml所有人都用统一配置。新功能任务先提 PRReview 时重点看命令是否可读、参数是否完备、描述是否准确。CI 里加一个自动化步骤跑anything validate来验证配置语法。这让新同事的入职流程从“人肉传文件”变成“拉一个仓库同步配置”缩短了上手时间。7.2 用 fzf 做交互式任务选择任务数量一旦突破三四十条anything list刷屏就不好找了。推荐和fzf配合使用把筛选放到面前来选择anything list | fzf --header选择要执行的任务这个命令让你在列表里模糊搜索选中即复制出任务名再配合anything run使用。有些终端用户会把这两个命令写成一个 shell 函数一键进入交互选择模式。7.3 与 AI 结合让自然语言直接驱动 CLI这是目前最有想象力的扩展方向甚至已经不算未来了。思路是给 AI 提供一个清晰的“技能清单”即 CLI-Anything 里所有任务的名称、参数、描述让 AI 用文本展示这些接口然后根据用户的自然语言直接生成并执行对应的anything命令。简单来说你可以把任务清单导出成一份喂给 AI 的上下文然后对它说“把生产数据库备份一下然后再清理三天前的备份。”AI 会拆解出要执行的步骤拼装出正确的命令甚至按顺序执行多个任务。这类“自然语言接口 命令行执行引擎”的组合正在和已有CLI工具生态打通CLI-Anything 因为所有任务都有统一的描述和参数结构天然适合被 AI 调用。我现在已经把任务清单直接绑定到一个 AI 技能协议里很多平时需要敲三四个命令步骤的工作现在用一句话就能触发整串流程。最后的个人体会从设计到真正用顺 CLI-Anything我最大的感受是“简单的东西反而最难做得优雅”。它不做复杂的逻辑编排不内置模板语言不引入需要学习的DSL所以它几乎没有学习曲线。但也正因为简单它的边界很清晰凡是值得重复的任务都以统一的入口沉淀下来凡是一次性的复杂度放回真正的脚本语言里。我现在的工具链里文件整理、数据库运维、代码发布、服务器管理等高频操作全部挂在 CLI-Anything 下面。换机器时也不怕丢“经验”同步一份配置就全部恢复。诚实地说这个工具不是银弹它不会自动帮你写好脚本也不会替你做系统设计但它确实解决了“我明明自动化了却还是记不住怎么用”的问题。如果你也正处于脚本满天飞、每个命令都得现场回忆的状态你值得花一个小时搭起来试试。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →