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