WezTerm Lua API 深入解析:LocalProcessInfo进程信息对象与前台进程探测实战
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
LocalProcessInfo是 WezTerm 向 Lua 配置脚本暴露的本地进程信息对象,它描述运行在本机上的进程及其子进程树,是mux-is-process-stateful事件回调与pane:get_foreground_process_info()方法的返回值类型。本文将以该对象为骨架,完整梳理其字段语义、平台限制,并深入结合 procinfo 模块源码 与 LocalPane 实现 讲解其底层数据来源,最后给出可复制的状态栏与关闭确认实战示例,帮助你在自己的 WezTerm 配置中安全、高效地使用进程信息。
什么是LocalProcessInfo
LocalProcessInfo(自 20220101-133340-7edc5b5a 版本起提供)表示运行在本地机器上的一个进程。WezTerm 通过它向 Lua 环境传递进程的 PID、父进程 PID、可执行文件路径、命令行参数、工作目录、运行状态以及完整的子进程树。
在 Rust 侧,该类型定义在 procinfo/src/lib.rs 中,并通过wezterm_dynamic的FromDynamic/ToDynamic派生与luahelper::impl_lua_conversion_dynamic!宏转换为 Lua 可读的对象,这就是配置脚本中可以直接用点号访问proc.pid、proc.argv等字段的原因。
字段一览
| 字段 | 类型 | 说明 |
|---|---|---|
pid | 整数 | 进程 ID |
ppid | 整数 | 父进程 ID |
name | 字符串 | 进程的短名称。受平台限制可能不准确或被截断,应优先使用executable或argv字段 |
status | 字符串 | 进程状态,取值见下文 |
argv | 表 | 进程的参数数组 |
executable | 字符串 | 可执行映像的完整路径(可能为空) |
cwd | 字符串 | 进程当前工作目录(可能为空) |
children | 表 | 以子进程 PID 为键、值为LocalProcessInfo对象的子进程表 |
在源码中,name字段的注释明确指出它对应进程的 COMM 名称,与可执行映像名不一定相同,进程运行时可自行修改(例如 Linux 上的setproctitle()),且许多系统会将其截断到 15~16 个字符,因此文档建议把executable和argv作为更可靠的判断依据。
status字段的类型在 Rust 侧为LocalProcessStatus枚举(见 procinfo/src/lib.rs),映射到 Lua 后的可取值为:
Idle、Run、Sleep、Stop、Zombie、Tracing、Dead、Wakekill、Waking、Parked、LockBlocked、Unknown。
并非所有取值在所有平台上都可用——例如 Linux 内核的R、S、Z、T、t、X/x等状态码会在 procinfo/src/linux.rs 中被映射为上述枚举,其他平台则可能只支持其中一部分。
children是一棵递归的进程树:键为子进程 PID,值为描述子进程的LocalProcessInfo对象,因此你可以像遍历树一样层层递归访问整棵进程层级。
数据来源与平台差异
LocalProcessInfo的获取入口是LocalProcessInfo::with_root_pid(pid),它从指定 PID 出发构建整棵进程树。需要明确的是,该信息仅对本地 pane 可用:多路复用(multiplexer)远程 pane、通过ssh连接的远程主机均无法获取远程进程信息。
各平台实现位于 procinfo crate 的模块文件中:
- Linux:procinfo/src/linux.rs
- macOS:procinfo/src/macos.rs
- Windows:procinfo/src/windows.rs
从源码结构看,Linux 实现完全基于/proc文件系统:枚举/proc下所有数值命名的目录得到 PID 列表,读取/proc/<pid>/stat解析进程名、状态、PPID 与启动时钟(starttime),通过read_link("/proc/<pid>/exe")获取可执行文件路径(procinfo/src/linux.rs),通过read_link("/proc/<pid>/cwd")获取工作目录(procinfo/src/linux.rs),通过拆分 NUL 字节分隔的/proc/<pid>/cmdline得到argv(procinfo/src/linux.rs),最后按ppid递归拼装出完整的进程树。
需要注意的是平台支持范围:
- 可执行路径查询(
executable):仅 Linux、macOS、Windows 支持;FreeBSD 等其他 Unix 系统目前不支持; - 非 Linux/macOS/Windows 平台:
with_root_pid、current_working_dir、executable_path三个方法直接返回None(见 procinfo/src/lib.rs); - 路径查询可能因 WezTerm 无法控制的各种原因失败(如权限、进程退出、/proc 不可用),此时字段为空;
- 查询进程信息存在一定运行时开销,过度使用可能拖慢 WezTerm(详见下文缓存机制)。
核心 API 一:pane:get_foreground_process_info()
pane:get_foreground_process_info()(自 20220624-141144-bd1b7c5d 起提供)返回当前 pane 中前台进程对应的LocalProcessInfo对象;若无法确定进程,则返回nil。
前台进程的判定规则
该方法的语义在不同平台上有显著差异,这是实际使用时最容易踩坑的地方:
- Unix 系统:查询的是进程组组长(process group leader),即终端前台进程组对应的进程;
- Windows:不存在进程组的概念,因此改为检查最初启动程序的进程树,并把"最近产生的后代进程"视为前台进程。
后者的实现可以在 mux/src/localpane.rs 中看到:divine_process_list以根进程为起点,递归遍历children,利用start_time字段比较各进程的相对"年龄",选出start_time最大的(即最近启动的)后代作为foreground,并将其children清空后缓存。Windows 上还会通过console句柄字段过滤(child.console == 0时跳过),以规避不相关控制台进程的干扰。
使用限制
- 仅本地 pane 可用;多路复用 pane、
ssh远程连接场景下无法获取; - 查询可执行路径仅限 Linux、macOS、Windows;
- 查询失败时返回
nil,脚本必须做好空值处理。
实战:状态栏显示前台进程
官方文档给出的示例将前台进程的 PID 与可执行文件 basename 显示在右侧状态栏:
local wezterm = require 'wezterm' -- 等价于 POSIX basename(3) -- 给定 "/foo/bar" 返回 "bar" -- 给定 "c:\\foo\\bar" 返回 "bar" function basename(s) return string.gsub(s, '(.*[/\\])(.*)', '%2') end wezterm.on('update-right-status', function(window, pane) local info = pane:get_foreground_process_info() if info then window:set_right_status( tostring(info.pid) .. ' ' .. basename(info.executable) ) else window:set_right_status '' end end) return {}basename函数同时兼容 POSIX 的/与 Windows 的\路径分隔符,这段配置在 Linux、macOS、Windows 上都能正确工作。由于get_foreground_process_info()可能返回nil,必须先判空再使用字段。
值得一提的是,WezTerm 内部对前台进程信息做了缓存与后台刷新处理:在 mux/src/localpane.rs 中,CachedLeaderInfo保存了前台进程 PID、可执行路径与工作目录,并带有 TTL 过期机制;源码注释还提到tcgetpgrp单次可能耗时约 700µs,若 10 个标签页都被鼠标扫过,仅取 PID 就可能累计 7ms 造成卡顿——这正是缓存存在的意义。因此"查询进程信息有运行时开销,应避免过度使用"的官方提示对应着真实的性能考量。
核心 API 二:mux-is-process-stateful事件
mux-is-process-stateful事件在多路复用层想要判断"某个 pane 是否可以无需用户确认直接关闭"时触发。
事件语义与返回值
- 该事件是同步的,回调必须尽快返回,以免阻塞多路复用器;
- 事件回调会收到一个代表该 pane 对应进程树的
LocalProcessInfo对象; - 返回值约定:
| 返回值 | 含义 |
|---|---|
true | 该进程树被视为有状态(stateful),关闭 pane 前应提示用户 |
false | 该进程树可以被直接终止,无需提示 |
nil | 使用默认行为:依据skip_close_confirmation_for_processes_named配置判定 |
| 其他任意值或出错 | 等价于返回nil |
Rust 侧的调用点在 mux/src/localpane.rs:can_close_without_prompting通过config::lua::emit_sync_callback同步调用 Lua 钩子,仅把Boolean和Nil识别为有效返回值,其余一律回退到默认逻辑,与文档描述完全一致。
默认判定逻辑(skip_close_confirmation_for_processes_named)
当事件返回nil或未定义时,WezTerm 使用skip_close_confirmation_for_processes_named配置项做判定。该配置(自 20210404-112810-b63a949d 起提供)列出被认为"无状态"、可安全关闭的进程名:
config.skip_close_confirmation_for_processes_named = { 'bash', 'sh', 'zsh', 'fish', 'tmux', 'nu', 'cmd.exe', 'pwsh.exe', 'powershell.exe', }关闭 pane 时,WezTerm 会检查该 pane 启动的程序所派生的所有进程名:如果全部进程名都命中列表,则不弹确认框;只要存在一个不在列表中的进程,就视为有状态并提示确认。默认判定逻辑在 mux/src/localpane.rs 中实现,值得注意两个细节:
- 它使用的是
flatten_to_exe_names()(见 procinfo/src/lib.rs),即把整棵进程树展开为"可执行文件 basename"的集合,再与配置列表比对; - 出于对 Fig 工具链的兼容,比较前会剥离进程名中
(figterm)后缀——Fig 的figterm伪终端夹在 shell 与终端之间,进程名形如<shell> (figterm),不剥离会导致判定永远失败。
若事件回调抛错,WezTerm 会记录错误日志并回退到默认行为(mux/src/localpane.rs),保证配置异常不会影响关闭功能。
实战:递归打印进程树
官方示例演示了如何用递归函数遍历children字段并缩进输出,通过wezterm.log_info记录进程树全貌。该示例不改变任何行为(返回nil走默认逻辑),但完整展示了对LocalProcessInfo各字段的读取方式:
local wezterm = require 'wezterm' function log_proc(proc, indent) indent = indent or '' wezterm.log_info( indent .. 'pid=' .. proc.pid .. ', name=' .. proc.name .. ', status=' .. proc.status ) wezterm.log_info(indent .. 'argv=' .. table.concat(proc.argv, ' ')) wezterm.log_info( indent .. 'executable=' .. proc.executable .. ', cwd=' .. proc.cwd ) for pid, child in pairs(proc.children) do log_proc(child, indent .. ' ') end end wezterm.on('mux-is-process-stateful', function(proc) log_proc(proc) -- 使用默认行为 return nil end) return {}对一个zsh启动bash、bash再启动vim foo的场景,输出日志形如:
INFO config::lua > lua: pid=1913470, name=zsh, status=Sleep INFO config::lua > lua: argv=-zsh INFO config::lua > lua: executable=/usr/bin/zsh, cwd=/home/wez INFO config::lua > lua: pid=1913567, name=bash, status=Sleep INFO config::lua > lua: argv=bash INFO config::lua > lua: executable=/usr/bin/bash, cwd=/home/wez INFO config::lua > lua: pid=1913624, name=vim, status=Sleep INFO config::lua > lua: argv=vim foo INFO config::lua > lua: executable=/usr/bin/vim, cwd=/home/wez递归遍历时注意proc.children以 PID 为键,pairs的遍历顺序在 Lua 中不保证稳定,若需要固定顺序应先对键排序。
进阶:基于进程信息定制关闭行为
把前面两块内容组合起来,可以构建一个比skip_close_confirmation_for_processes_named更精细的关闭策略。例如:默认对vim、git(可能正处在交互式操作中)等进程保持提示,而对其他无状态进程直接关闭:
local wezterm = require 'wezterm' -- 若进程树中存在需要保护的有状态程序,则要求提示 local STATEFUL = { vim = true, nvim = true, emacs = true, git = true, } local function tree_has_stateful(proc) if STATEFUL[proc.name] or STATEFUL[(proc.executable:match('([^/\\]+)$') or '')] then return true end for _, child in pairs(proc.children) do if tree_has_stateful(child) then return true end end return false end wezterm.on('mux-is-process-stateful', function(proc) if tree_has_stateful(proc) then return true end return false end) return {}这个示例展示了mux-is-process-stateful的核心价值:它收到的proc是整棵进程树(根节点),你拥有比内置默认逻辑更自由的判定空间。同时它也是同步回调,递归遍历深进程树时注意控制复杂度,避免拖慢多路复用器。
总结与常见陷阱
LocalProcessInfo是 WezTerm 连接"终端 UI 层"与"操作系统进程层"的桥梁,掌握它就能在状态栏展示前台程序、按进程内容定制关闭确认策略。使用时的要点可归纳为:
- 字段优先级:
name可能被截断或被setproctitle()篡改,关键判定用executable与argv; - 平台边界:仅本地 pane 有数据;
executable路径查询仅限 Linux/macOS/Windows;Windows 的"前台进程"由进程树中最年轻的后代推断而来,与 Unix 的进程组组长语义不同; - 空值处理:
get_foreground_process_info()返回nil、executable/cwd可能为空,脚本需判空; - 性能意识:进程查询有运行时开销,WezTerm 内部通过 TTL 缓存缓解(相关实现见 mux/src/localpane.rs),自定义脚本也应避免在热点回调(如
update-right-status的每次触发)中高频重建进程树; - 事件返回值:
mux-is-process-stateful只认true/false/nil,其余值一律回退默认逻辑;回调出错会回退默认逻辑,因此配置错误不会造成关闭功能失效,但请留意错误日志。
相关文档可继续阅读 mux-is-process-stateful 事件、pane:get_foreground_process_info() 与 skip_close_confirmation_for_processes_named 配置,完整的 Lua API 索引位于 docs/config/lua。
【免费下载链接】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),仅供参考