WezTerm 用户变量(User Vars)完全指南:pane:get_user_vars()与 OSC 1337 数据通道
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
本文围绕 WezTerm 的pane:get_user_vars()API 展开,讲解如何通过 iTerm2 风格的 OSC 1337SetUserVar转义序列,在 shell 与 WezTerm 配置(Lua)之间建立一条可靠的“用户变量”数据通道。读完本文,你将掌握从 shell 侧写入、在配置侧读取用户变量的完整流程,并能基于user-var-changed、update-status事件与多路复用(multiplexer)传播机制,实现诸如动态标签标题、状态栏自定义信息等实战方案。
一、什么是用户变量(User Vars)
pane:get_user_vars()是 Pane 对象上提供的一个方法,用于返回一个 Lua table,其中保存着已经赋值给该 pane 的所有用户变量(user variables)。该方法自版本20210502-130208-bff6815d起可用。
用户变量在语义上与环境变量(environment variables)有些相似,但有几个关键区别:
- 它们作用域是终端 pane,而非操作系统进程;
- 运行在 pane 里的应用程序只能写入、不能读取它们;
- 只有 WezTerm 自身(以及你的 Lua 配置)才能读取。
用户变量由 iTerm2 定义、WezTerm 同样支持的转义序列来设置。由于它走的是标准的转义序列通道,因此跨平台、跨进程模型都可用:无论是本地 pane、SSH 远程 pane,还是通过 tmux 嵌套运行(需启用 tmux 透传),都能正常工作。
在源码层面,用户变量的存储位置是终端状态中的一张HashMap:term/src/terminalstate/mod.rs 中声明了user_vars: HashMap<String, String>,并通过 term/src/terminalstate/mod.rs 的user_vars()访问器对外提供只读访问。
二、设置用户变量:OSC 1337 SetUserVar 转义序列
WezTerm 对操作系统中规定的转义序列(Operating System Command,OSC)进行解析。其中 OSC 1337 的SetUserVar子命令专门用于设置用户变量,格式为:
ESC ] 1337 ; SetUserVar=<name>=<base64-encoded-value> BEL即\033]1337;SetUserVar=名字=Base64编码后的值\007(\007为 BEL 终止符,也可使用\033\\形式的 ST 终止符)。值必须经过 base64 编码,名称则保持明文。
在解析侧,wezterm-escape-parser/src/osc.rs 中实现了对keyword == "SetUserVar"的匹配:以=分割出名称与值,对值调用base64_decode解码后构造出ITermProprietary::SetUserVar { name, value }。该解析器还带有单元测试(见 wezterm-escape-parser/src/osc.rs),使用SetUserVar=foo=aGVsbG8=这样的样例验证了往返编码与解码逻辑。
2.1 在 shell 中封装设置函数
为了在日常使用中方便地设置用户变量,官方文档提供了一个 shell 函数__wezterm_set_user_var。该函数同样被收录在 WezTerm 的 shell integration 脚本 assets/shell-integration/wezterm.sh 中:
# 该函数发射 OSC 1337 序列,为当前终端 pane 设置用户变量。 # 它要求 PATH 中存在 base64 工具。 # 该函数包含在 wezterm 的 shell integration 脚本中,此处为清晰起见单独复现。 __wezterm_set_user_var() { if hash base64 2>/dev/null ; then if [[ -z "${TMUX}" ]] ; then printf "\033]1337;SetUserVar=%s=%s\007" "$1" `echo -n "$2" | base64` else # 使用 tmux 透传转义序列(详见 tmux FAQ) # 注意:同时需要在 tmux.conf 中添加 "set -g allow-passthrough on" printf "\033Ptmux;\033\033]1337;SetUserVar=%s=%s\007\033\\" "$1" `echo -n "$2" | base64` fi fi } __wezterm_set_user_var "foo" "bar"代码要点:
- 检测 base64:
hash base64确保工具存在,避免在精简环境中报错; - 区分 tmux 场景:当环境变量
TMUX非空时,说明当前运行在 tmux 内部,需要用 tmux 的 passthrough 转义序列\033Ptmux;\033...\033\\将 OSC 1337 原样透传给外层终端。使用该方式前,还需在tmux.conf中启用set -g allow-passthrough on; echo -n去除换行:避免把换行符编进 base64 结果。
如果直接使用一条命令,也可以写成(来自 user-var-changed 与 passing-data 配方 中的等价写法):
printf "\033]1337;SetUserVar=%s=%s\007" foo `echo -n bar | base64`这将把名为foo的用户变量设置为bar。
2.2 base64 换行的注意事项
在某些系统上,base64命令默认会在输出一定长度后进行换行包裹,从而限制值的最大长度。如果遇到值被截断或解析异常的情况,可以给 base64 加上不换行参数,例如-w 0(GNU 版本)或-b 0(BSD 版本)。
三、读取用户变量:pane:get_user_vars()
设置好用户变量之后,就可以在 WezTerm 的 Lua 配置中读取它。在任意拿到pane对象的地方(如各种事件回调中),调用pane:get_user_vars()即可返回包含全部用户变量的 table:
wezterm.log_info('foo var is ' .. pane:get_user_vars().foo)get_user_vars()返回的是一个键值对 table,键是用户变量名(字符串),值同样是字符串。访问不存在的键会得到nil,因此实践中常用or '默认值'或(var or '')来兜底。
3.1 源码级实现
get_user_vars在 Lua API 层注册于 lua-api-crates/mux/src/pane.rs,通过methods.add_method("get_user_vars", ...)绑定,实际调用底层 pane 的copy_user_vars()方法:
methods.add_method("get_user_vars", |_, this, _: ()| { let mux = get_mux()?; let pane = this.resolve(&mux)?; Ok(pane.copy_user_vars()) });注意其命名为copy_user_vars(复制语义):它返回的是用户变量集合的一个快照副本,而非对内部状态的引用,这保证了 Lua 侧拿到的是一个不受后续变更影响的独立 table。
对于本地 pane,copy_user_vars的实现位于 mux/src/localpane.rs,直接克隆终端状态中的用户变量表:
fn copy_user_vars(&self) -> HashMap<String, String> { self.terminal.lock().user_vars().clone() }而对于通过多路复用协议连接的远端 pane,则在 wezterm-client/src/pane/clientpane.rs 中有对应的客户端侧实现,保证在 SSH 远程 pane 上同样可以读取到用户变量。
四、设置用户变量触发的事件链
在 pane 中设置(或修改)用户变量并非“写入即止”,它还会在包含该 pane 的窗口内联动触发一系列事件与 UI 更新,具体包括:
- user-var-changed:变量被设置或修改时直接触发,允许你立即采取行动。该事件自版本
20220903-194523-3bb1ed61起可用; - update-status:触发左右状态栏(status items)的更新,可在状态栏中展示用户变量信息;
- 标题与标签栏(title / tab bar)区域随之更新,并在更新过程中触发与之关联的其他事件;
- 多路复用客户端传播:用户变量的变更事件会传播到所有已连接的多路复用(multiplexer)客户端,因此在远端窗口、多客户端场景下同样保持一致。
user-var-changed事件的回调签名与使用示例如下(来自 user-var-changed):
local wezterm = require 'wezterm' wezterm.on('user-var-changed', function(window, pane, name, value) wezterm.log_info('var', name, value) end) return {}当 shell 侧执行SetUserVar把foo设为bar时,该处理器会被调用,参数分别为name = 'foo'、value = 'bar'。
五、实战:让用户变量真正为你所用
5.1 利用 shell integration 自动注入的内置变量
安装并启用 shell integration 之后,脚本 assets/shell-integration/wezterm.sh 会自动为每个 pane 设置一组内置用户变量,无需手工编写任何代码:
| 变量名 | 含义 | 设置时机 |
|---|---|---|
WEZTERM_PROG | 当前前台程序的名称 | 每次执行命令时(也用于在命令退出后清空) |
WEZTERM_USER | 当前登录用户名(id -un) | 会话初始化时 |
WEZTERM_HOST | 主机名(Linux 读取/proc/sys/kernel/hostname,macOS 用hostname,也可从WEZTERM_HOSTNAME环境变量取) | 会话初始化时 |
WEZTERM_IN_TMUX | 是否运行在 tmux 内(1/0) | 会话初始化时 |
例如:
__wezterm_set_user_var "WEZTERM_USER" "$(id -un)" __wezterm_set_user_var "WEZTERM_HOST" "$(cat /proc/sys/kernel/hostname)"这些内置变量可以直接在配置中通过pane:get_user_vars()读取,用于状态栏、标签标题等展示场景。
5.2 追踪前台程序:PROG 变量与动态标签标题
passing-data 配方 给出了一个非常经典的用法:通过 alias + trap 在 shell 中维护一个PROG用户变量,记录当前正在运行的程序,然后据此定制 tab 标题。
shell 侧(使用前面定义好的__wezterm_set_user_var):
function _run_prog() { # 将 PROG 设为正在运行的程序名 __wezterm_set_user_var "PROG" "$1" # 程序结束时清除它 trap '__wezterm_set_user_var PROG ""' EXIT # 执行对应命令,注意用 command 避免与 alias 定义循环 command "$@" } alias vim="_run_prog vim" alias tmux="_run_prog tmux" alias nvim="_run_prog nvim"wezterm 侧,在format-tab-title事件中读取user_vars.PROG来拼接标签标题:
local wezterm = require 'wezterm' wezterm.on('format-tab-title', function(tab) local prog = tab.active_pane.user_vars.PROG return tab.active_pane.title .. ' [' .. (prog or '') .. ']' end) return {}注意这里的tab.active_pane.user_vars来自 PaneInformation 结构中的user_vars字段——这是获取用户变量的另一条等价途径,与pane:get_user_vars()返回相同的底层数据。在format-tab-title这类以tab为入参的事件里使用该字段往往比自行持有 pane 引用更直接。
5.3 在状态栏中展示用户变量
借助update-status事件与pane:get_user_vars(),可以在左右状态栏中实时展示变量。例如:
local wezterm = require 'wezterm' wezterm.on('update-status', function(window, pane) local vars = pane:get_user_vars() local host = vars.WEZTERM_HOST or 'unknown' local user = vars.WEZTERM_USER or '' window.set_right_status(wezterm.format { { Text = user .. '@' .. host }, }) end) return {}由于设置用户变量会触发update-status事件,状态栏能够在变量变化时自动刷新,无需手动轮询。
六、注意事项与适用边界
- 必须由 shell 主动配合:用户变量只能由 pane 内的程序通过转义序列写入,WezTerm 不会凭空产生它们。除了安装 shell integration 自动注入的内置变量外,其他变量都需要你自行在 shell 配置(如
.bashrc、.zshrc、alias、函数)中安排发射对应的 OSC 1337 序列。这是该机制唯一需要付出的“成本”; - base64 依赖:设置函数依赖
base64工具存在于 PATH 中,且注意部分系统 base64 输出的换行包裹问题(可用-w 0等参数规避); - tmux 透传:在 tmux 内使用时必须使用 tmux passthrough 转义序列,并在
tmux.conf中开启set -g allow-passthrough on,否则转义序列会被 tmux 吞掉; - 版本要求:
pane:get_user_vars()需要版本20210502-130208-bff6815d及以上;user-var-changed事件需要版本20220903-194523-3bb1ed61及以上; - 跨客户端传播:用户变量变更事件会传播到所有连接的多路复用客户端,这让远端 pane、多窗口场景下的状态同步成为可能;但请记住,这依赖逃逸序列链路的完整透传(包括经过 SSH、tmux 等中间层)。
七、与其他方案的关系
用户变量并非 WezTerm 中 pane → 配置信息传递的唯一途径,但它是最通用、跨场景能力最强的一个。在同一主题下,仓库还提供了这些可对比参考的机制:
- OSC 0/1/2 标题序列:用于设置窗口标题与标签标题,可用
pane:get_title()读取; - OSC 7 工作目录序列:用于上报当前工作目录,可用
pane:get_current_working_dir()读取; - 本地进程探测(
pane:get_foreground_process_info()等):不需要修改 shell 配置即可获取前台进程信息,但仅对本地进程有效,无法用于 SSH 远程或多路复用场景。
相比之下,用户变量是其中少数能“穿透” SSH 与多路复用连接的方案,且用途完全由你定义——这正是它作为 pane 与配置间“自定义信号通道”的价值所在。更多对比细节可参考 passing-data 配方。
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考