今天在 GitHub Trending 上看到一个有意思的项目:witr,一个帮你回答"这个进程/端口/容器为什么在跑"的跨平台命令行工具——只需一个命令,就能把整个启动链路追根溯源呈现出来。
一、项目概述
Linux 上排查进程,通常要配合 ps、top、lsof、ss、systemctl、docker ps 等一堆工具,各自输出片段化信息,剩下的全靠人脑关联。但现实场景往往是这样的:某个 Node 进程占满 CPU,你想知道它是谁启动的、容器还是 systemdPM2、亦或是 SSH 会话里的手动运行——现有工具无法直接回答这个问题。
witr 正是为了解决这个痛点而生的:把一切"在跑的东西"都还原成 PID,再顺着 PID 追溯出完整的因果链,直接告诉你「为什么会存在这个进程」。
核心特性:
- 支持按进程名、进程 ID、端口、容器名、打开的文件五种方式查询
- 自动解析 systemd / launchd / PM2 / Docker / Kubernetes / 容器(Docker/Podman/nerdctl/LXC/LXD/FreeBSD Jails)等多种 Supervisor 和容器运行时
- 输出进程启动链路:
systemd (pid 1) → pm2 (pid 5034) → node (pid 14233) - 支持交互式 TUI(类似 htop 的终端界面),分进程、端口、容器、文件锁四个 Tab,可鼠标操作
- 跨平台:Linux、macOS、Windows、FreeBSD
- 输出 JSON 格式,方便脚本调用
- 支持 Shell 自动补全(Bash / Zsh / Fish / PowerShell)
二、技术原理
2.1 核心设计理念
witr 的设计哲学很清晰:一切都是 PID 问题。无论是端口、容器还是文件名,最终都映射到一个进程 ID;一旦有了 PID,就可以通过系统调用追溯其父进程,递归构建出完整的因果链。
端口 5000 → nginx (pid 2311) → systemd (pid 1)
容器 redis → containerd-shim → dockerd → systemd (pid 1)
2.2 源码结构
项目使用 Go 编写(go 1.25),关键源码结构如下:
主入口 (cmd/witr/main.go):
package main
import (
"github.com/pranshuparmar/witr/internal/app"
"github.com/pranshuparmar/witr/internal/version"
)
func main() {
app.SetVersion(version.Version, version.Commit, version.BuildDate)
app.Execute()
}
构建方式(Makefile):
BINARY := witr
CMD := ./cmd/witr
build:
CGO_ENABLED=0 go build -o $(BINARY) $(CMD)
关键细节:
CGO_ENABLED=0表示纯静态编译,无需任何 C 运行时依赖,这也是 witr 能做成单文件绿色分发的原因。
TUI 界面依赖 charmbracelet/bubbletea(Go 生态著名的 TUI 框架),提供交互式进程树浏览。
依赖模块(go.mod)中值得注意的几个关键依赖:
| 依赖 | 用途 |
|---|---|
charmbracelet/bubbletea | 交互式 TUI |
charmbracelet/lipgloss | 终端样式渲染 |
coreos/go-systemd/v22 | systemd 进程链解析 |
godbus/dbus/v5 | D-Bus 通信,查询 systemd |
spf13/cobra | CLI 参数解析 |
golang.org/x/sys | 跨平台系统调用(Linux/macOS/Windows/FreeBSD) |
2.3 退出码设计
witr 为脚本化使用设计了语义化的退出码:
| 退出码 | 含义 |
|---|---|
| 0 | 进程找到,无警告 |
| 1 | 进程找到,有警告(如以 root 运行、监听公网接口等) |
| 2 | 未找到匹配进程 |
| 3 | 权限不足 |
| 4 | 参数错误或匹配结果模糊 |
| 5 | 内部错误 |
这对在 CI/CD 脚本或监控告警中集成 witr 非常友好:
witr nginx --short
case $? in
0) echo "All clear" ;;
1) echo "Warnings detected" ;;
2) echo "Process not running" ;;
5) echo "Internal error" ;;
esac
2.4 多容器运行时统一抽象
witr 的容器查询能力覆盖了 Docker、Podman、nerdctl、Kubernetes (crictl)、Incus、LXC、LXD 和 FreeBSD Jails,底层通过检测 PATH 中的运行时 CLI 来自动发现,并统一抽象为一个容器列表——不需要记住每个运行时各自的命令。
三、安装与快速开始
3.1 一键安装(推荐)
Linux / macOS / FreeBSD:
curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh | bash
Windows(PowerShell):
irm https://raw.githubusercontent.com/pranshuparmar/witr/main/install.ps1 | iex
安装脚本会自动检测操作系统、CPU 架构,安装到
/usr/local/bin/witr,并顺便装好 man page。
3.2 包管理器安装
witr 已进入多个主流包管理器:
# Homebrew (macOS/Linux)
brew install witr
# APT (Debian/Ubuntu)
sudo apt install witr
# Conda/mamba/pixi (跨平台)
conda install -c conda-forge witr
# 或
pixi global install witr
# npm (跨平台)
npm install -g @pranshuparmar/witr
# Winget (Windows)
winget install -e --id PranshuParmar.witr
# Go 安装
go install github.com/pranshuparmar/witr/cmd/witr@latest
3.3 验证安装
witr --version
man witr
四、使用方法与实战
4.1 基础查询
按进程名查询(模糊匹配):
witr node
输出示例:
Target : node
Process : node (pid 14233)
User : pm2
Command : node index.js
Started : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)
Why It Exists :
systemd (pid 1) → pm2 (pid 5034) → node (pid 14233)
Source : pm2
Working Dir : /opt/apps/expense-manager
Git Repo : expense-manager (main)
Sockets : 127.0.0.1:5001 (TCP | LISTENING)
清晰到不需要任何额外工具,就能定位到进程是 PM2 启动的,而 PM2 本身由 systemd 管理。
按端口查询:
witr --port 5000 --short
# 输出:systemd (pid 1) → PM2 v5.3.1: God (pid 1481580) → python (pid 1482060)
按 PID 查询:
witr --pid 143895 --tree
systemd (pid 1)
└─ init-systemd(Ub (pid 2)
└─ SessionLeader (pid 143858)
└─ Relay(143860) (pid 143859)
└─ bash (pid 143860)
└─ sh (pid 143886)
└─ node (pid 143895)
├─ node (pid 143930)
├─ node (pid 144189)
└─ node (pid 144234)
树形视图直观展示了进程及其子进程的关系。
4.2 交互式 TUI
不带参数直接运行,或加 -i 启动交互式界面:
witr
# 或
witr -i
TUI 有四个 Tab:
- Processes:实时可排序/过滤的进程列表,选中后在侧边栏显示祖先树
- Ports:监听端口及对应进程,
a键切换显示全部端口 - Containers:聚合 Docker、Podman、K8s、LXC 等所有容器运行时
- Locks:系统级文件锁(Linux / macOS / FreeBSD),
a键合并所有打开的文件
TUI 支持鼠标操作、自动检测终端背景色(明/暗主题自适应),还有自适应刷新频率。
💡 不想安装?项目提供了浏览器在线体验版,可以直接在网页上试玩模拟环境。
4.3 脚本化与 CI 集成
JSON 格式输出适合在脚本中处理:
witr nginx --json | jq '.ancestry[] | .name'
# ["systemd","nginx"]
在告警脚本中使用退出码:
#!/bin/bash
witr --port 443 $*
if [ $? -eq 0 ]; then
echo "Port 443 正常运行"
elif [ $? -eq 2 ]; then
echo "端口未监听,触发告警!"
fi
4.4 环境变量与详细诊断
# 查看进程的环境变量(Linux,macOS 受 SIP 限制)
witr --pid 1234 --env
# 显示警告信息(root 运行、监听公网、高内存占用等)
witr nginx --warnings
# 显示扩展进程信息(工作目录、Socket、文件上下文等)
witr --pid 1234 --verbose
五、常见问题与解决方案
Q1:提示 “Permission denied”
witr 读取 /proc(Linux)或调用系统 API,部分操作需要更高权限。Linux / macOS 上使用 sudo:
sudo witr [your arguments]
Windows 上以管理员身份运行 PowerShell。
Q2:macOS 上环境变量显示不完整
这是 macOS System Integrity Protection (SIP) 的限制,root 权限也无法读取部分受保护进程的环境变量,属于正常行为。
Q3:TUI 终端背景色识别不准确
witr 自动检测终端背景色。如果颜色不匹配,可以在调用时加 --no-color 关闭颜色输出。
Q4:容器查询不到结果
确保目标容器运行时 CLI 在 PATH 中。witr 支持 Docker、Podman、nerdctl、K8s crictl、Incus、LXC、LXD、FreeBSD jails 任一工具在 PATH 中即可自动识别。
Q5:多进程名模糊匹配
默认使用子字符串模糊匹配(nginx 会匹配 nginx 和 enginx)。用 -x/--exact 精确匹配:
witr nginx -x
Q6:想在 CI/CD 流水线中使用
建议使用 --json 输出并结合退出码判断。官方已为 Aqua’s aqua 工具链制作了包,可在 CI 中通过 aqua 安装:
# aqua.yaml
registries:
- type: standard
ref: semver(v0.1.0)
packages:
- name: pranshuparmar/witr
六、总结
witr 解决的是一个真实且高频的系统诊断痛点:当你面对一个"不知道谁启动的进程"时,传统工具只能给你碎片化的状态信息,而 witr 把「是什么 → 怎么来的 → 谁负责」串联成一条因果链,一句话就说清楚。
作为一个用 Go 编写的单文件静态二进制,它在跨平台覆盖度、Supervisor 解析深度(TUI 支持 systemd timers、launchd schedules、tmux/screen 等)、安装便利性(支持 20+ 种包管理器)上都下了不少功夫,值得加入日常工具箱。
GitHub 仓库: https://github.com/pranshuparmar/witr
在线试用: https://pranshuparmar.github.io/witr/