oh-my-fish 包(Plugin)开发实战:从 `omf new` 脚手架到 Hooks 事件系统与发布全流程
oh-my-fish 包Plugin开发实战从omf new脚手架到 Hooks 事件系统与发布全流程【免费下载链接】oh-my-fishThe Fish Shell Framework项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-fish导读本文以 oh-my-fish 官方文档 docs/pt-BR/Packages.md 为骨架讲解在 Oh My FishThe Fish Shell Framework中从零创建并发布一个 fish 插件/主题的完整流程使用omf new生成标准目录结构、编写函数文件、利用五大 Hooksinit、key_bindings、install、update、uninstall在生命周期各阶段挂载自定义行为最终将包提交到官方公共仓库。读完本文你将掌握包命名规范、目录布局、Hook 触发时机与可用变量并能结合源码理解 Oh My Fish 底层是如何调用这些钩子的。一、包的创建用omf new生成脚手架1.1 包命名规则在开始之前先明确命名约束包名只能包含小写英文字母单词之间用连字符-分隔例如hello_world这种带下划线的写法其实并不符合“仅小写字母与连字符”的严格要求文档示例中为了可读性保留了它但官方推荐形如hello-world的命名。源码层面Oh My Fish 在 pkg/omf/functions/packages/omf.packages.valid_name.fish 中做了更严格的校验名称不得为omf或default这两个是保留名用于核心框架与默认主题名称必须以小写字母开头a-z名称中不得包含/、空格、、、!、%、#等特殊字符。不满足以上任一条件omf new会直接报错$name is not a valid package/theme name并返回$OMF_INVALID_ARG。1.2 生成模板结构Oh My Fish 内置脚手架工具一条命令即可生成包的完整目录骨架。以创建一个提供hello_world命令的插件为例$ omf new pkg hello_world如果是主题则使用omf new theme my_theme_name。值得注意的是命令行的选项别名在 pkg/omf/functions/packages/omf.packages.new.fish 中p、plugin、pkg都会归一化为插件类型而t、th、the、thm、theme、themes则归一化为主题类型因此英文文档里的omf new plugin hello_world与葡萄牙语文档里的omf new pkg hello_world完全等价。命令执行成功后工具会创建目录并自动将当前工作目录切换到新包目录下此时查看目录内容$ ls -l README.md init.fish functions/hello_world.fish completions/hello_world.fish部分语言版本文档的清单略有差异但核心文件一致。这四个文件的职责分别是文件作用README.md描述包的用途与工作原理社区约定每个包都必须编写init.fish初始化 Hookshell 首次加载包时执行详见下文 Hooks 章节functions/hello_world.fish包对外暴露的函数入口completions/hello_world.fishfish 自动补全脚本为命令提供 Tab 补全建议请务必为你的命令提供自动补全。fish 官方对complete命令有完整文档omf new生成的completions/目录就是为此准备的。1.3 脚手架背后的实现细节从源码看omf new并不是简单复制文件而是一个带模板变量替换的生成器首先通过 pkg/omf/functions/packages/omf.packages.new.fish 中的__omf.packages.new.mkdir确定创建位置优先使用$OMF_CONFIG目录否则回退到$OMF_PATH目录随后从模板目录pkg/omf/templates/pkg/主题为pkg/omf/templates/themes/递归复制文件在复制过程中用sed依次替换模板占位符{{USER_NAME}}取自git config user.name、{{GITHUB_USER}}取自git config github.user、{{NAME}}包名、{{YEAR}}当前年份文件名中的{{NAME}}.fish会被重命名为真实的包名文件例如functions/{{NAME}}.fish→functions/hello_world.fish。模板的默认内容见 pkg/omf/templates/pkg/functions/{{NAME}}.fishfunction {{NAME}} -d My package # Package entry-point end生成的functions/hello_world.fish即对应文档中的示例函数function hello_world -d Prints hello world echo Hello World! end1.4 函数组织规范一函数一文件注意命名空间文档特别强调两点工程规范值得开发者严格遵守每个函数必须声明在functions目录下独立的一个文件中。这是 fish 自动加载autoloading机制的硬性要求fish 按需加载函数只有在首次被调用时才读取对应文件从而避免在 shell 启动时加载大量未使用的函数、拖慢启动速度。这正是 Oh My Fish 把函数拆分为独立文件的原因。fish 没有私有作用域。如果你需要把包拆分成多个内部函数必须通过前缀避免与其他包、其他命令的命名冲突推荐用包名做前缀例如hello_world_print_help更彻底的方案是用双下划线前缀标识“私有”函数例如__function_name_print_help以明确告知这是内部实现、不构成公共 API从而避免污染全局命令命名空间。二、Hooks包生命周期事件系统Hooks 是 Oh My Fish 提供给包作者的“事件挂载点”Hook 本质上是普通的 fish 脚本文件名与触发它的事件名一致绝大多数位于包项目目录下的hooks/子目录中。包可以利用 Hooks 实现高级安装逻辑、自定义资源管理等能力。需要特别注意的通用约定Hook 执行时的工作目录始终是包根目录。这一点在 pkg/omf/functions/packages/omf.packages.run_hook.fish 中有直接体现源码先pushd $path再source钩子脚本执行完毕后popd还原。在启动时被调用的 Hookinit.fish、key_bindings.fish会拖慢 shell 启动务必避免在其中编写慢代码如果包根本不需要某个 Hook 文件请直接删掉它保持包轻量。Oh My Fish 目前支持五个 Hook按触发时机可分为“启动期”init、key_bindings与“生命周期期”install、update、uninstall两类。2.1initshell 首次加载时执行一次initHook 在 shell 第一次加载包时运行一次脚本位于包根目录的init.fish注意不在hooks/子目录。模板生成的 pkg/omf/templates/pkg/init.fish 已注释说明可用的变量。在该 Hook 内部你可以访问三个包相关变量变量含义$package包名称$path包的安装路径$dependencies包的依赖列表举例若init.fish内容为echo hello_world initialized那么每次打开新终端顶部都会出现一行hello_world initialized。initHook 的典型用途包括修改环境变量、加载资源、自动加载函数等。即便你的包不导出任何函数也可以利用这个事件为包增加功能甚至动态创建函数。2.2key_bindings自定义按键绑定如果包或主题需要使用快捷键必须在key_bindingsHook 中配置脚本位于包根目录的key_bindings.fish。在这个 Hook 中你可以自由使用 fish 的bind命令定义自定义按键绑定。两个值得注意的细节主题同样可以定义快捷键。Oh My Fish 在切换主题时会重新加载按键绑定——pkg/omf/functions/themes/omf.theme.set.fish 中可以看到切换主题后调用__fish_reload_key_bindings重载key_bindings.fish的逻辑。由于它属于启动期 Hook同样应避免在其中放置耗时操作。2.3install首次安装时触发installHook 在包首次被安装时触发脚本位于hooks/install.fish。在该 Hook 中可以访问两个变量变量含义$package包名称$path包的安装路径它适合做下载额外资源、初始化 Git 子模块、安装第三方依赖如 Bash 脚本等一次性工作。从 pkg/omf/functions/packages/omf.packages.install.fish 源码可以看到安装流程的顺序克隆仓库 → 判定包类型插件/主题主题会移动至$OMF_PATH/themes/→ 写入 bundle 记录 → 安装 bundle 依赖 →最后执行omf.packages.run_hook $install_dir install。这意味着installHook 是安装动作的收尾环节一旦 Hook 执行失败返回非零状态码整个安装会被判定为失败。2.4update更新后触发updateHook 在包被更新后触发脚本位于hooks/update.fish可访问的变量与install相同$package、$path。它的典型用途是更新 Git 子模块、检查第三方依赖的新版本。pkg/omf/functions/packages/omf.packages.update.fish 中通过omf.packages.run_hook $target_path update触发该钩子。2.5uninstall删除前清理uninstallHook 在包通过omf remove pkg被删除之前触发脚本位于hooks/uninstall.fish可访问$package、$path两个变量用于清理自定义资源。源码 pkg/omf/functions/packages/omf.packages.remove.fish 完整还原了删除流程也印证了文档中两个重要兼容性说明兼容旧布局如果包根目录存在uninstall.fish旧式位置同样会被执行——源码中omf.packages.run_hook $path uninstall之后还会检查并source $path/uninstall.fish兼容事件监听除了直接放置脚本包还可以通过 fish 事件机制监听uninstall_$pkg或{$pkg}_uninstall两个事件名来响应卸载——源码中对应emit uninstall_$pkg与emit {$pkg}_uninstall两行。此外删除主题时若被删主题正是当前主题Oh My Fish 会把主题自动重置为default。2.6 Hook 触发机制的源码解读所有 Hook 的统一执行入口是 pkg/omf/functions/packages/omf.packages.run_hook.fishfunction omf.packages.run_hook -a path hook set -l hook_script $path/hooks/$hook.fish set package (basename $path) if test -e $hook_script pushd $path source $hook_script set -l code $status popd return $code end end这段实现揭示了几个关键事实除了init.fish、key_bindings.fish包根目录与兼容的根目录uninstall.fish外其余 Hook 统一约定存放在hooks/hook名.fish$package变量直接由basename $path推导得到执行前先pushd $path保证 Hook 内工作目录始终是包根目录使用source而非子进程执行因此 Hook 中的echo、变量赋值等会直接影响当前 shell 会话Hook 的退出状态码会被透传install等流程会据此判断成败。三、发布你的包开发完成后如何让全世界或者说社区的 Oh My Fish 用户安装你的包文档给出的答案是官方公共包注册表由oh-my-fish/packages-main仓库统一管理。你需要参照该仓库的 README 说明把你的包加入官方包数据库。中文版文档补充了本地注册的方式Oh My Fish 会在$OMF_PATH/db/目录登记已发布的插件或主题使用omf submit命令可以把你的仓库地址写入本地注册索引# 插件 omf submit pkg/hello_world .../hello_world.git # 主题 omf submit theme/my_theme .../my_theme_name.git注意omf submit只修改本地注册索引要让包进入官方数据库最终仍需向官方注册仓库提交合并请求PR并入官方索引。注册完成后其他用户即可通过omf install 包名安装你的包安装流程会依次走完“克隆仓库 → 识别类型 → 写入 bundle → 执行installHook”的完整链路你的包便正式进入 Oh My Fish 生态。四、小结围绕 docs/pt-BR/Packages.md其他语言版本见 docs/en-US/Packages.md 与 docs/zh-CN/Packages.md本文覆盖了包开发的三个核心环节创建omf new pkg name/omf new theme name生成脚手架遵循小写字母与连字符的命名规范、一函数一文件的 autoload 约束、带前缀避免命名冲突的工程约定生命周期五大 Hooksinit、key_bindings、install、update、uninstall分别在启动、按键绑定、安装、更新、卸载时机挂载脚本统一由omf.packages.run_hook在包根目录上下文执行支持包根目录旧式布局与 fish 事件的向后兼容发布通过omf submit写入本地注册索引最终向packages-main官方注册仓库提交并入官方数据库。掌握了这些内容你就可以开始编写自己的第一个 Oh My Fish 插件并用 Hooks 为它赋予安装引导、按键绑定与资源清理等高级能力。【免费下载链接】oh-my-fishThe Fish Shell Framework项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-fish创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

阿里云ECS部署Oracle 19c RAC实战指南:AFD+私网直连+静默安装

阿里云ECS部署Oracle 19c RAC实战指南:AFD+私网直连+静默安装

简介:本资源是一份面向DBA、云平台运维工程师及Oracle高可用架构实践者的实战手册,聚焦阿里云ECS环境下CentOS 7.6系统部署Oracle 19c双节点RAC集群的全流程——从硬件选型、存储与网络规划,到Grid Infrastructure安装、ASM磁盘组配置、数据库…

2026/9/23 16:58:01 阅读更多 →
ASP.NET C#大型ERP源码实战解析:库存并发、审批流与部署避坑

ASP.NET C#大型ERP源码实战解析:库存并发、审批流与部署避坑

简介:这是一套基于ASP.NET与C#开发的大型综合管理系统源码,面向中高级.NET开发者及企业级后台系统学习者,适用于ERP、OA、CRM等通用管理平台的二次开发与架构参考。资源包大小为52.88MB,虽未提供具体文件总数与类型明细&#xff0…

2026/9/23 16:57:00 阅读更多 →
Java大作业双人联机森林冰火人源码拆解:Swing+Socket实现

Java大作业双人联机森林冰火人源码拆解:Swing+Socket实现

简介:这份资源是面向高校计算机相关专业学生的Java课程设计参考项目,主题为双人联机小游戏森林冰火人,适合作为期末大作业、课程设计或毕业设计的完整实现方案,也适合刚接触Java游戏开发、希望理解联机逻辑与图形界面编程的初学者…

2026/9/23 16:57:00 阅读更多 →

最新新闻

边缘计算控制器到底值不值?算清数据搬运费、时延与安全三笔账

边缘计算控制器到底值不值?算清数据搬运费、时延与安全三笔账

这几年跑工业现场,被问得最多的一个问题是:边缘计算控制器到底是不是厂商在炒概念?我每次都不急着给答案,而是先让对方把传统方案的三笔账算一算。算完账,大多数人都沉默了——原来自己一直在为数据的搬运费、等待费&a…

2026/9/24 23:02:55 阅读更多 →
六年Intel Mac免费换新M5?售后置换逻辑与老用户升级指南

六年Intel Mac免费换新M5?售后置换逻辑与老用户升级指南

1. 从一台六年前的Intel Mac说起:这件事为什么能引爆讨论先把事情本身说清楚。一台2019年前后入手的Intel芯片Mac,用了六年,按常理早就过了标准保修期,甚至已经进入"维修成本接近残值"的阶段。这种机器一旦出问题&#…

2026/9/24 23:02:54 阅读更多 →
学生成绩学分制管理系统设计与实现:从业务规则到数据库落地

学生成绩学分制管理系统设计与实现:从业务规则到数据库落地

第一次拿到“学生成绩学分制管理系统的设计与实现”这个题目,很多同学的判断是:这不就是一个带登录的增删改查吗?先建几张表、写个接口、套个前端模板,能跑就完事了。但你要真抱着这个心态去做,开题答辩大概率没问题&a…

2026/9/24 23:02:54 阅读更多 →
开发Android手机安全管家:权限审计与RSA+AES数据加密实战

开发Android手机安全管家:权限审计与RSA+AES数据加密实战

1. 研究思路:为什么需要一套“手机安全管家”智能手机早已不只是通讯工具了。微信里躺着工作群消息,相册里存着身份证照片,备忘录里记着银行卡号,甚至很多人的支付类App还开着免密小额支付。换句话说,手机就是数字身份…

2026/9/24 23:02:54 阅读更多 →
Zblog响应式主题开发实战:从免费主题定制到性能优化

Zblog响应式主题开发实战:从免费主题定制到性能优化

1. 项目概述与选型分析1.1 为什么在众多博客程序里选了Zblog做个人博客这件事,最难的其实不是写作,而是选一套顺手、够轻、不折腾的程序。我这些年玩过WordPress、Typecho、Hexo,最后长期留在Zblog上,原因很简单:PHP程…

2026/9/24 23:02:54 阅读更多 →
电化学原位FTIR实战指南:ATR原理、界面信号捕获与谱图解析

电化学原位FTIR实战指南:ATR原理、界面信号捕获与谱图解析

1. 为什么FTIR不是“拍张红外照片”那么简单?——电化学场景下你必须懂的底层逻辑傅里叶红外光谱(FTIR)在电化学表征中常被当作“标配工具”,但很多人拿到谱图后第一反应是:这峰在哪?怎么跟文献对不上&…

2026/9/24 23:01:53 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →