你有没有过这种时刻:服务器上 443 端口突然被占了,你花了 15 分钟,串起了 ss -tlnp → lsof -i :443 → ps aux | grep 9876 → systemctl status → docker ps,最后终于找到了那个 kubectl port-forward 的后台残留。
然后你对着终端发呆:我就想知道是谁启动了它,需要跑 4 个命令吗?
witr 这个工具,就是为了终结这种局面而生的。
从"它是什么"到"它为什么存在"
ps、top、lsof、ss、systemctl、docker ps——这些工具都没问题,但它们只回答 what,不回答 why。
你调了 4 个工具,手动拼出因果链:systemd 启动了 pm2,pm2 启动了 node,node 打开了 5001 端口。witr 把这些拼起来,一条命令给你。
它的名字来自开发者 Pranshu Parmar 最常问自己的那句话:“Why is this running?”
这种命名方式让我想起另一个经典工具 htop——简单粗暴,但一看到就懂。
一个命令能干什么
witr 的核心输入维度有 5 个:进程名、PID、端口、文件、容器。你可以单独用,也可以混着来。
# 查一个进程的因果链
witr node
# 精确匹配(避免 substring 误杀)
witr --exact nginx
# 按端口查
witr --port 5432
# 按 PID 查
witr --pid 12345
# 查某个文件被谁持有
witr --file /var/run/docker.pid
# 查容器背后的进程
witr --container myapp
# 全部混用
witr nginx --port 443 --pid 2311 --verbose
输出长这样:
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)
Restarts : 1
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)
Listening : 127.0.0.1:5001
一个页面,你就知道:谁启动的、什么时候启动的、重启了几次、代码在哪个目录、对应哪个 git 分支、监听什么端口。这就是 witr 的价值——把原来 4 个命令的输出,折叠成 15 行。
TUI 模式:交互式仪表盘
witr 还有一个 -i 交互模式,启动后是一个分 4 个 tab 的终端仪表盘:
- Processes — 实时进程列表,可搜索
- Ports — 端口占用一览
- Containers — 容器进程映射
- Locks — 文件锁状态
按 a 切到"所有打开文件"视图,按 / 搜索,按回车看详情。这种设计让我想到 btop 的思路:同样一个工具,CLI 模式给脚本用,TUI 模式给人用。
6 种输出模式,覆盖从人到脚本的所有场景
witr node # 默认:完整信息
witr --short node # 只显示因果链,适合 grep/awk
witr --tree node # 树形结构,带子进程
witr --json node # JSON,喂给其他工具
witr --verbose node # 扩展信息(环境变量、警告等)
witr --warnings node # 只显示有问题的进程
这种设计很务实:人在终端看默认输出,脚本管道吃 JSON,监控告警吃 --warnings + exit code。witr 的退出码也很有用:
| 代码 | 含义 |
|---|---|
| 0 | 进程找到,无异常 |
| 1 | 有警告 |
| 2 | 未找到 |
| 3 | 权限不足 |
| 4 | 输入无效或模糊匹配 |
可以直接用在 CI 里判断"服务是否在跑"。
容器时代的新痛点
Docker 环境里这个问题更严重。容器内的 PID 和宿主机 PID 完全不同,进程名字还经常被 entrypoint.sh 包装。
# witr 直接跨运行时搜索
witr --container myapp
witr --container nginx --verbose
支持 Docker、Podman、nerdctl、K8s crictl 和 FreeBSD jails,它会跨容器运行时匹配容器名、镜像、命令和 compose project/service 标签。
而且 witr 还处理了一个边界情况:systemd socket activation。当 systemd 自己占用了一个端口,但实际服务跑在容器里时,witr 会做 port → container fallback,不会让你误以为是 systemd 在提供服务。
这种细节说明作者是真正的运维痛点患者——不是从文档出发做工具,是从自己的痛苦出发。
安装:一条命令的事
witr 是单二进制静态编译,Linux、macOS、FreeBSD、Windows 全覆盖。安装方式多到你没法抱怨"我装不了":
# 官方安装脚本(推荐)
curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh | sh
# Homebrew
brew install witr
# Conda
conda install -c conda-forge witr
# Winget(Windows)
winget install PranshuParmar.witr
# NPM
npm install -g @pranshuparmar/witr
# Nix
nix run github:pranshuparmar/witr
# 直接从源码编译
go install github.com/pranshuparmar/witr/cmd/witr@latest
二进制体积很小,Go 静态编译,复制到任意目录就能跑。没有依赖,没有 runtime,没有 virtualenv。
同类工具横评
| 工具 | 语言 | 核心能力 | 短板 |
|---|---|---|---|
| witr | Go | 因果链 + 多输入维度 + TUI | 无 |
| lsof | C | 文件/端口/网络打开列表 | 只列不分析 |
| ss | C | socket 统计 | 无因果链 |
| fuser | C | 谁占了端口/PID | 输出简陋 |
| systemctl | C | systemd 服务管理 | 只看 systemd |
| docker ps | Go | 容器列表 | 只看容器 |
其他工具都是"列数据",witr 是"讲故事"。你不需要自己拼,它帮你拼好。
谁该用 witr
- 运维/SRE:排查线上进程异常时,省掉 4 个命令的组合拳
- 后端开发者:本地调试时想知道"为什么 3000 端口被占了"
- DevOps/平台工程师:容器编排场景下跨运行时做进程映射
- 安全工程师:快速识别异常进程的来源链
如果你发现自己每周至少有一次用到 lsof + ss + ps + systemctl 的组合,witr 值得装。
最后
witr 的 README 里写了它的成功标准:一个 10 年经验的 Linux 运维,在 30 秒内搞清楚为什么一个进程在跑。
我测了一下。原来的流程大概 3-5 分钟(取决于日志的复杂度),用 witr 大概 10 秒。差距不大,但每天省 5 分钟,一年就是 30 小时——够看两季美剧了。
github.com/pranshuparmar/witr
Apache-2.0 | 17.9k ⭐ | Go
相关阅读:
- desktop-search-tools-comparison-2026 — 另一类"帮我找东西"的工具