今天在 GitHub Trending 上看到一个有意思的项目:witr,一个帮你回答"这个进程/端口/容器为什么在跑"的跨平台命令行工具——只需一个命令,就能把整个启动链路追根溯源呈现出来。

一、项目概述

Linux 上排查进程,通常要配合 pstoplsofsssystemctldocker 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/v22systemd 进程链解析
godbus/dbus/v5D-Bus 通信,查询 systemd
spf13/cobraCLI 参数解析
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 会匹配 nginxenginx)。用 -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/