iTerm2 Shell Integration 新 Shell 接入全流程清单:从脚本打包到自动注入的完整实现指南
2026/9/21 7:40:26 网站建设 项目流程

iTerm2 Shell Integration 新 Shell 接入全流程清单:从脚本打包到自动注入的完整实现指南

【免费下载链接】iTerm2iTerm2 is a terminal emulator for Mac OS X that does amazing things.项目地址: https://gitcode.com/gh_mirrors/it/iTerm2

导读

本指南是 iTerm2 官方为开发者编写的一份 App 端操作清单,讲解如何为一种全新 shell添加 shell integration(shell 集成)支持。围绕这份清单,本文从脚本打包、版本注册、枚举登记、自动注入、手动安装器 UI、测试与文档六个层面,逐条展开其背后的源码实现与调用链,读者可按此清单完整落地一次 "添加新 shell" 的改动。全文中<shell>表示小写 shell 名(即ShellIntegrationVersion=...;shell=<shell>中出现的名字),Xxx表示对应的首字母大写形式(用于枚举 case 与类名)。最完整的先例是 xonsh 接入时的两个提交:1bf85d898("Add xonsh as a supported shell for shell integration")与1e4ee607b("Add xonsh shell integration"),下文各步骤不明确时均可对照它们。

注意:清单中的行号是近似值、会随版本漂移,实际操作请以 grep 相邻符号为准。

前提条件:脚本仓库与两类 shell 形态

动手之前需要先明确两件事:

  1. 脚本本体不在本仓库。shell integration 脚本位于iTerm2-shell-integration子模块(独立仓库),必须先在该子模块中合并新脚本并完成打包(见下节"打包脚本"),App 侧依赖它的步骤才能生效。
  2. 决定脚本如何被自动加载。shell 分为两种形态:
    • dotfile 型(bash / zsh / tcsh):安装器在用户的 dotfile(如~/.bashrc)末尾追加一行source
    • autoload 型(fish / xonsh 及类似 shell):shell 会自动从某个目录加载脚本,无需修改 dotfile。注入方式改为设置一个标记/配置环境变量——xonsh 用XONSHRC指向脚本;另一些 shell 则是设置其 autoload 目录。动手前必须先确认你的 shell 使用哪个环境变量,因为下面多个步骤都以它为中心。

打包脚本:让 App 认识这个文件

tools/copy_shell_integration.sh是唯一的打包入口,脚本会从子模块拷贝脚本、并自动生成版本表头。四个必做改动:

  • tools/copy_shell_integration.sh:仿照现有五个脚本,添加一行cp $SUBMODULE/shell_integration/<shell> Resources/shell_integration/iterm2_shell_integration.<shell>。当前打包出的脚本位于 Resources/shell_integration(iterm2_shell_integration.{bash,fish,tcsh,xonsh,zsh}五个文件)。

  • submodules/iTerm2-shell-integration:把子模块指针 bump 到包含新脚本的提交。

  • iTerm2.xcodeproj/project.pbxproj:将iterm2_shell_integration.<shell>添加为文件引用并纳入各 target 的 Resources 构建阶段(xonsh 当时新增了 1 个 PBXFileReference + 3 个 "in Resources" 构建文件)。必须使用tools/add_file_to_xcodeproj.rb,禁止手改工程文件。

  • sources/ShellIntegration/iTermLatestVersionByShell.h不要手工编辑。该文件首行就是// DO NOT MODIFY - Generated by tools/copy_shell_integration.sh,由copy_shell_integration.sh通过 grep 每个打包脚本里的ShellIntegrationVersion=自动再生成:

    grep ShellIntegrationVersion= ... | sed -n 's/^.*ShellIntegrationVersion=\([^;]*\).*shell=\([a-z]*\).*$/ @"\2": @\1,/p'

    当前生成结果可见于 sources/ShellIntegration/iTermLatestVersionByShell.h:bash @20fish @22tcsh @8xonsh @2zsh @17。生成后@"<shell>"条目会自动出现,其值以脚本实际声明为准——不要假定是 1:例如 xonsh 初始提交写的是@1,但脚本后来 bump 到版本 2、表头重新推导后,现在读的是@2

    脚本侧有一个硬性前置条件:必须输出能被生成器正则匹配的 token,即包含ShellIntegrationVersion=<N>;shell=<shell>且 shell 名为小写[a-z]*。xonsh 的脚本就以注释形式满足此条件,见 Resources/shell_integration/iterm2_shell_integration.xonsh 的# ShellIntegrationVersion=2;shell=xonsh。若脚本省略该 token 或 shell 命名不一致,表头条目会被静默丢弃、该 shell 退化为无版本跟踪——依赖表头前务必先验证打包后的脚本包含此 token

枚举与 shell 注册表

App 侧用枚举表示每种 shell,需在两处登记:

  • sources/ShellIntegrationInstaller/iTermShellIntegrationInstaller.h:在iTermShellIntegrationShell枚举中加入iTermShellIntegrationShellXxx(现有 case 可见iTermShellIntegrationShellXonsh)。再次强调:版本表头iTermLatestVersionByShell.h不在这里编辑,它是生成文件。
  • sources/ShellIntegrationInstaller/iTermShellIntegrationWindowController.m
    • (a)iTermShellIntegrationShellString返回@"<shell>"
    • (b) shell 名到枚举的map(约 610 行)添加@"<shell>": @(iTermShellIntegrationShellXxx),现有条目如@"xonsh": @(iTermShellIntegrationShellXonsh)

shell 版本通知表

terminalSetShellIntegrationVersion:解析脚本上报的版本字符串并据此弹升级提醒,位于 sources/VT100Screen/VT100ScreenMutableState+TerminalDelegate.m。两点改动:

  • 更新该方法中过时的 shell 名单注释(约 2704 行)以包含新 shell。
  • 不要把新 shell 加入硬编码的 critical-updates 字典——除非该 shell 存在即使在"仅关键更新"模式下也必须催促用户升级的版本。全新 shell 应保持缺席:其最新版本已由生成表头在非关键路径上提供。源码中该字典目前仅含tcsh/bash/zsh/fish,而 xonsh 不在其中,正是这一原则的体现。

自动注入:登录 shell 与 SSH 两条链路

ShellIntegrationInjection.swift:资源列表、枚举与注入类

文件 sources/ShellIntegration/ShellIntegrationInjection.swift 负责在启动进程时通过修改环境变量注入集成脚本,三处改动:

  • (a) 在资源列表(约 167 行)加入local("iterm2_shell_integration.<shell>"),当前列表含bash/zsh/fish/xonshbash-si-loader.zshenv、fish 的vendor_conf.d加载器。
  • (b) 在Shell枚举(约 181 行)加case xxx = "<shell>",并在工厂 switch 中返回对应的注入类。
  • (c) 新增XxxShellIntegrationInjection类:设置该 shell 的标记/配置环境变量,让 shell 自动加载脚本。xonsh 的实现在约 282-304 行:把脚本路径追加到冒号分隔的XONSHRC列表头部("\(shellIntegrationDir)/iterm2_shell_integration.xonsh:\(existing)");而目录式 autoload 的 shell 则把 autoload-dir 环境变量指向包含脚本的目录——请确认该目录确实含有一个命名正确、shell 会去 source 的脚本

关键不变量computeModified(env:argv:)必须原样返回argv(仅改环境),与 xonsh 类的return (modifiedEnvironment(...), argv)完全一致。一旦它改动 argv,下面 Conductor 的空 argv SSH 分支就不再适用,集成会在 SSH 场景下静默失败。若你的 shell 确实需要改 argv(如 bash 的--posix),则必须在该代码中显式点名该 shell。

Conductor.swift:SSH 场景两处编辑

文件 sources/SSH/Conductor.swift 处理 SSH 会话中的注入,两处改动:

  • alwaysSupported(约 2080 行)加入"<shell>",当前值为["fish", "xonsh", "zsh"],使 SSH 注入对远端登录 shell 生效。
  • 注释列举仅环境注入的 shell(约 2344 行)写的是 "(zsh/fish/xonsh add no argv)"。若你的 shell 是纯环境式,加入该列表。该注释解释了空 argv 分支(约 2332-2344 行)为何正确:纯环境注入时保持modifiedCommandArgs为空,让 framer 以交互模式exec 登录 shell 并继承注入的环境(如 zsh 的ZDOTDIR);若在此处点名 shell,framer 会执行login_shell -c "<shell>",外层非交互 shell 会在内层交互 shell 启动前拆掉注入(典型如 zsh 的.zshenv会 unsetZDOTDIR),集成便永不加载。此分支的正确性恰恰依赖于上面"argv 不变"不变量。

手动安装器 UI 流程

手动安装(菜单 iTerm2 > Install Shell Integration)由iTermShellIntegrationWindowController.m驱动,dotfile 型 shell 大多可复用现有 switch;autoload 型则需要 xonsh 式的 "无 dotfile" 特判。需覆盖以下要点(行号取自该文件,均已在 xonsh 提交中验证):

  • shellIntegrationPath:脚本写入位置。dotfile 型为~/.iterm2_shell_integration.<shell>;autoload 型为 autoload 路径,如 xonsh 返回~/.config/xonsh/rc.d/iterm2.xsh(见约 275 行)。注意 macOS 与 Linux 配置目录的差异。
  • utilities:dotfile 型追加别名;PATH 型改为追加 PATH 行(xonsh 用$PATH.insert(...)),并在别名构建器的case ...Xxx:返回nil(见约 292 行注释)。
  • shell_and/引号 switch(约 331 行):在匹配该 shell&&拼接与引号行为的分支中加case ...Xxx:
  • autoload 特判mkdir -p <autoload dir>步骤(xonsh 为mkdir -p ~/.config/xonsh/rc.d),需要在字符串构建器(约 413/417 行)与reallySend路径(约 516/521 行)各加一次。
  • launchBashString/exitBashString单 bash 特例:仅当新 shell 需要 xonsh 那样的 prompt 设置 workaround(用bash -c设置 PS1/PS2,见约 462 行)。
  • modifyStartupScriptsAndProceedTo:early-return 空操作,以及两处finishSendShellCommandsInstall快捷路径(约 648、709、721 行):autoload 型 shell 跳过 dotfile 步骤,注释明确写着 "For xonsh, no dotfile modification is needed (rc.d auto-loads)"。

配套的粘贴命令视图 iTermShellIntegrationPasteShellCommandsViewController.m 还需两处改动:

  • (a) 更新 "not supported" 提示文案,把新 shell 列入支持名单(现有文案为 "Only bash, fish, tcsh, xonsh, and zsh work with shell integration",约 68 行)。
  • (b) autoload 型需添加 "auto-loads ... no dotfile update needed" 步骤与isDone快捷判断(xonsh:约 111-138 行,isDone = (stage > i) || (stage >= i && self.shell == iTermShellIntegrationShellXonsh))。

其他 shell 名单触点:全局一致性

新增 shell 名会散落在多个功能入口,必须同步更新以免出现"某处支持、某处报不支持"的不一致:

  • sources/API/iTermAPIScriptLauncher.m:向knownShells(约 818 行,当前@[ @"bash", @"tcsh", @"zsh", @"fish", @"xonsh" ])添加@"<shell>",使 Python API 脚本能以该登录 shell 运行。
  • sources/Settings/ProfilesGeneralPreferencesViewController.m两个列表都要改(xonsh 提交同时更新了它们):
    • shells数组(约 793 行,当前@[ @"bash", @"fish", @"xonsh", @"zsh"]):启用自定义命令的 "Load shell integration automatically" 选项。
    • SSH 命令类型的提示文案(约 836 行),当前为"Requires bash, fish, tcsh, xonsh, or zsh."(用户可见),按字母序插入新 shell。若只改数组不改文案,SSH 帮助文本会错误地宣称该 shell 不受支持。
  • OtherResources/framer.pyget_env_var(约 596-618 行)通过os.environ.get('SHELL')检测 shell,两处编辑:
    • known_shells(约 604 行,含 bash/csh/dash/fish/ksh/sh/tcsh/xonsh/zsh)添加<shell>

    • 仿照 xonsh 的$SHELL覆盖(约 601-602 行,以XONSHRC为标记),新增基于该 shell 标记环境变量的覆盖:

      if os.environ.get('<MARKER_ENV_VAR>', ''): user_shell = '<shell>'

      这一步对任何非 POSIX / 非登录 shell 场景都是必须的,而非可选项:当 shell 以自定义 profile 命令启动、经 SSH 启动、或任何非登录 shell 路径运行时,$SHELL仍指向用户的 POSIX 登录 shell(如/bin/zsh),basename($SHELL)会误判。xonsh 正因XONSHRC需要此覆盖。验证方式:以非登录自定义命令启动该 shell,确认 framer 日志报告shell=<shell>

  • sources/AITerm/RemoteCommand.swift:更新getShellType帮助文案中的示例列表(外观性,见约 678 行 "Detects the shell in use (e.g., bash, fish, xonsh, zsh).")。
  • sources/Settings/Base.lproj/PreferencePanel.xib:更新 "Load shell integration automatically" 复选框的 tooltip 中 shell 列表(约 2448 行,当前为 "Available for bash, fish, tcsh, xonsh and zsh. ..."),同样为外观性改动。

测试与文档:让改动可验证

  • ModernTests/ShellIntegrationLiveHarness.swift:给SupportedShell枚举中每一个 switch添加该 shell(xonsh 当年触及五个位置,均可在文件约 93-133 行看到对应 case):
    • 枚举case列表(约 94 行,含zsh, bash, fish, tcsh, xonsh)。
    • 可执行候选(约 103 行,如 xonsh 为["/usr/local/bin/xonsh", "/opt/homebrew/bin/xonsh", "/usr/bin/xonsh"])。
    • arguments(for:)启动参数(约 108/114 行,xonsh 为[executable, "--no-rc", "-i"])——务必为 shell 定向路由,而不是落入为其他 shell 合并的臂。
    • 资源名(约 124 行,xonsh 为iterm2_shell_integration.xonsh)。
    • sourceCommand(integrationPath:)(约 128-133 行):该 switch 穷尽且无 default,不添加新臂无法编译。使用该 shell 自身的 source 语法:zsh/bash/fish/tcsh 用source <path>,xonsh 用execx(open('<path>').read());注意存在source要求解析期常量路径的 shell。
    • 若该 shell 的行编辑器在 source 前发送光标位置报告(CSI 6n,如 xonsh 的 prompt_toolkit),harness 必须应答,否则 shell 卡死;xonsh 目前因此XCTSkip(见文件约 80-88 行),新 shell 大概率需要同样的 skip 或实现 CPR 应答器。
  • docs/notes-3.7.txt:追加一行 release note,限 50 列(xonsh 为 "xonsh is now a supported shell.")。

不需要改动 / 仅需验证

  • sources/Settings/Profiles/ITAddressBookMgr.m只有注释提到 shell 名——可选择性扩充注释,无需代码改动。
  • 引号转义提交9aff539d4是为语法会破坏反斜杠转义的 shell(xonsh)准备的通用使能器;检查为新手 shell 生成的任何安装器命令字符串是否需要同类处理。

小结:一次新增 shell 的完整改动地图

将以上清单归纳为一张改动地图:打包层copy_shell_integration.sh+ 子模块指针 + pbxproj + 生成表头)→App 识别层iTermShellIntegrationInstaller.h枚举 +iTermShellIntegrationWindowController.m字符串/映射 +VT100ScreenMutableState+TerminalDelegate.m版本通知)→自动注入层ShellIntegrationInjection.swift注入类 +Conductor.swiftSSH 支持,牢记"argv 不变"不变量)→手动安装 UI 层(window controller 各分支 + paste 命令视图)→全局名单层(API launcher、Profile 偏好面板、framer.py、RemoteCommand 文案、PreferencePanel tooltip)→测试与文档层ShellIntegrationLiveHarness.swift的穷尽 switch + release notes)。对照 xonsh 的两个先例提交逐条执行,即可稳妥地为新 shell 打通 iTerm2 的全部集成能力。

【免费下载链接】iTerm2iTerm2 is a terminal emulator for Mac OS X that does amazing things.项目地址: https://gitcode.com/gh_mirrors/it/iTerm2

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询