news 2026/9/19 14:04:04

ani-cli 源码解析(一):670 行 POSIX Shell 如何构建一个完整 CLI 应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ani-cli 源码解析(一):670 行 POSIX Shell 如何构建一个完整 CLI 应用

ani-cli 源码解析(一):670 行 POSIX Shell 如何构建一个完整 CLI 应用

【免费下载链接】ani-cliA cli tool to browse and play anime项目地址: https://gitcode.com/gh_mirrors/an/ani-cli

ani-cli 是一个在终端里搜索、播放和下载动漫(anime)的命令行工具,它没有依赖任何框架,整个应用就是一个 670 行的 POSIX Shell 脚本。本文将从源码结构、参数解析、抓取流程和跨平台适配四个角度,带你拆解这个"极简但完整"的 Shell CLI 应用是如何写出来的。

一个文件就是一个完整 CLI 应用

传统观点认为:做一个像样的 CLI 需要选语言、选框架、管依赖。ani-cli 打破了这个印象——整个项目主体就是这一个可执行脚本:

  • 主程序:ani-cli(670 行,#!/bin/sh开头,兼容 POSIX sh)
  • 手册页:ani-cli.1
  • Homebrew 配方:Formula/ani-cli.rb
  • 二次开发指南:hacking.md

想本地跑起来的话:

git clone https://gitcode.com/gh_mirrors/an/ani-cli sudo cp ani-cli/ani-cli /usr/local/bin

📌 为什么坚持 POSIX Shell?答案在 CONTRIBUTING.md 里:项目要求通过shfmtshellcheck -s sh(严格 POSIX 模式)检查,并且明确写着"No extra dependencies unless absolutely necessary"。极小的体积带来一个巨大好处——随处可跑:Linux、macOS、Windows (Git Bash/WSL)、Android Termux、iOS iSH、Steam Deck,甚至 FreeBSD。

快速总览:670 行的功能分区

打开 ani-cli 你会发现它按注释被清晰地切成了六大区块,这是阅读长脚本最好的"地图":

区块行号范围职责
# UIani-cli#L5-L44交互菜单、彩色输出、帮助信息
# Bookkeepingani-cli#L108-L148自动更新、依赖检查、清理
# SCRAPINGani-cli#L150-L308搜索、剧集列表、m3u8 解密
# HISTORYani-cli#L310-L340观看历史(续看功能的数据基础)
# PLAYINGani-cli#L342-L437多播放器适配、分集播放
# MAINani-cli#L439-L670全局配置、参数解析、主流程

项目官方文档 hacking.md 甚至为整套交互设计了一张状态机式的 UX 规格图,搜索 → 选片 → 选集 → 播放 → 后菜单的完整闭环一目了然:

命令行参数解析:一个 while + case 搞定所有选项

很多语言需要引入 argparse、clap 这类库,Shell 的"标准答案"是while循环 +case模式匹配。ani-cli#L492-L560 用一个约 70 行的解析循环覆盖了全部 17 个选项:

while [ $# -gt 0 ]; do case "$1" in -q | --quality) quality="$2"; shift ;; -c | --continue) source=history ;; ... *) query="...$1" ;; # 非选项参数拼成搜索词 esac shift done

几个值得学习的设计:

  1. 长短选项等价-q | --quality写在同一个分支里,零成本支持两种风格;
  2. 位置无关[ $# -lt 2 ] && die "missing argument!"对带参数的选项做校验,让用户可以把选项放在查询词前后(帮助信息里明确承诺了这一点,见 ani-cli#L48-L51);
  3. 剩余参数即查询*)分支把所有非选项参数合并成一个查询字符串,空格转+,天然实现"多词搜索";
  4. 无框架的 help/version-h输出由printf模板生成(ani-cli#L46-L101),-V直接读第 3 行的version_number变量——版本号只有一处定义,自更新时靠sed提取它(ani-cli#L113),保证全局一致。

💡 这种"case 表驱动"的写法,是所有 Shell CLI 项目最值得照抄的参数解析范式。

交互菜单:三层抽象让 fzf / rofi / dmenu 互换

ani-cli 的交互核心是两个函数:

  • menu()(ani-cli#L9-L16):按menu_program分发到 fzf / rofi / dmenu 或任意自定义程序,每个后端的参数差异被压缩进一个case
  • nth()(ani-cli#L19-L35):接收"序号 + Tab + 数据"格式的标准输入,让用户选一行或一段范围,再统一切出结果字段。

整个 UI 层因此与具体菜单程序解耦:环境变量ANI_CLI_MENU、命令行--rofi/--dmenu都能切换,甚至连"非终端环境"(如管道调用)都会自动降级到 dmenu(ani-cli#L479-L482)。播放结束后还有"后菜单"循环(ani-cli#L635-L653):next / replay / previous / select / change_quality / quit,同样复用nth(),一行代码都不用重写。

抓取流程:从搜索词到 m3u8 的四步管道

hacking.md 里有一张官方流程图,概括了 ani-cli 作为爬虫的完整链路:

对应到代码,每一步都是一个"小函数 + sed 正则"的组合:

  1. 搜索:ani-cli#L199-L210 请求搜索页后,用三级sed管道把 HTML 压平、按film-detail切块、抽取id \t 标题
  2. 剧集列表:ani-cli#L213-L219 从剧集页抽出id \t 集数
  3. 取流地址:ani-cli#L222-L242 是最硬核的一步——内嵌播放器把配置做成了base64(json XOR 密钥),deobfuscate_blob() 用od取字节、在子 shell 里逐字节异或还原出 JSON,再从中抠出 m3u8 地址和字幕;
  4. 选清晰度:select_quality() 按best/worst/1080等规则从"分辨率 > URL"列表里挑一条。

🔍 值得注意的是防封处理:hianime_curl() 封装了带超时的 curl,检查 HTTP 状态码并识别 Cloudflare 的"Just a moment"拦截页;curl 本身也会按curl_firefox135 → curl_chrome136 → … → curl的顺序自动降级选择(dep_ch_failover),普通用户无感。

跨平台适配与自更新:Shell 项目的两个硬骨头

平台差异用一个uname分发集中解决(ani-cli#L457-L469):

  • macOS:默认播放器 IINA(Apple 上 mpv 的友好替代);
  • Android (Termux):android_mpvam start拉起 mpv 应用,referrer 通过共享配置文件传递(ani-cli#L355-L362);
  • Windows/WSL:直接用mpv.exe
  • iOS (iSH):输出 OSC8 超链接,点一下在 VLC 打开(ani-cli#L399-L402);
  • Linux:mpv → flatpak mpv → vlc 逐级回退。

play_episode()里那段按播放器分发的case(ani-cli#L371-L404)则展示了另一个技巧:不同播放器传参风格完全不同(mpv 用--sub-file、VLC 用:input-slave=、IINA 要转义冒号),统一收口在一个函数里,按名字模式匹配分派,比抽象类更"Shell"。

自更新则是 Shell 圈的经典骚操作:-U拉取远端脚本 →diff对比自己($0)→ 用patch原地修补(update_script())。没有包管理器、没有下载器,脚本自己给自己打补丁。

写在前面:这套写法能学到什么

ani-cli 证明了 Shell 不是"临时脚本语言":清晰的区块注释、单一数据格式(id \t name贯穿全链路)、case表驱动、failover 依赖探测、trap清理(ani-cli#L581),这些模式组合起来就是一个可维护的完整应用。

如果你也想改它的抓取逻辑或移植到新站点,建议从官方指南 hacking.md 入手,其中给出了搜索页、剧集页、播放器三处改造的完整方法论;而 ani-cli.1 则是一份适合快速查选项的手册页。

下一篇我们计划深入deobfuscate_blob()的异或解密细节与观看历史系统,敬请期待。

【免费下载链接】ani-cliA cli tool to browse and play anime项目地址: https://gitcode.com/gh_mirrors/an/ani-cli

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 14:02:13

51单片机简易计算器设计:矩阵键盘与数码管动态显示实现

简介:面向单片机课程设计与电子设计初学者的完整项目文档,内容围绕基于80C51/AT89S52单片机的简易计算器设计展开,从系统开发背景、设计目的到硬件选型与软件编程均有系统说明。文档重点介绍LCD1602液晶显示、4*4矩阵键盘以及AT89S52最小系统…

作者头像 李华
网站建设 2026/9/19 14:00:43

nvm-windows实战指南:Node多版本安装、切换与配置

1. nvm是什么,为什么Windows开发者离不开它1.1 多版本共存的真实痛点先讲一个我自己的经历。有一年我在维护一个老后台管理系统,用的Vue 2 Webpack 4,锁定的Node版本是14.x。与此同时,新接手的自动化脚本项目要求Node 18以上&…

作者头像 李华
网站建设 2026/9/19 13:59:51

S7-1200流水灯PLC程序拆解:移位循环与上电延时原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华