witr v0.3.4 常用 CLI 命令详解

以六个真实排障场景实测 witr v0.3.4:端口占用、不明进程、容器归属、文件锁、批量巡检与 JSON 自动化,附命令速查表与三条排障实录。

witrTrace tracking可观测性2026/10/25

witr v0.3.4 常用 CLI 命令详解

目标:以真实服务器上的六个高频排障场景为主线,逐条实测 witr v0.3.4 的命令组合——端口占用排查、不明进程溯源、容器归属确认、文件锁定位、进程树巡检、JSON 自动化集成,并把实测中踩过的坑沉淀为排障实录。全部命令与回显均在 Ubuntu 24.04.5 LTS(x86_64)真实服务器上执行取得;参数语义与旗标全集见《witr v0.3.4 配置参数详解》,安装见《Ubuntu 24.04 安装 witr v0.3.4 指南》。

一、环境信息

本文所有命令在以下环境逐条实测,回显为实际输出:

项值
操作系统Ubuntu 24.04.5 LTS(x86_64,systemd 在管,运行 SSH、cron 等常规服务)
witr 版本v0.3.4(commit 7e46fa68,2026-10-04 构建),安装于 /usr/local/bin/witr
运行用户root
演示负载python3 -m http.server 8141(临时 HTTP 服务)、一个跑 python:3.12-alpine 的演示负载(进程名 python,监听 8142)
配套工具jq(JSON 解析演示用)、systemctl(取单元主 PID)

💡 说明:文中的 PID、端口与会话状态是实测时点的真实快照。你的服务器上 PID 必然不同,跟着操作时先用 witr <名字> 或 ss -ltnp 找到自己的目标,再套用相同命令组合。

二、命令体系总览

witr 只有一条主命令,所有行为靠旗标组合。按「查什么 × 怎么呈现」画成速查地图:

查什么命令骨架典型呈现
进程名witr <名字> / witr <名字> -x标准报告;多匹配列候选(exit 4)
进程号witr --pid <PID>标准报告,可加 --verbose 深挖
端口witr --port <端口>标准报告 + 套接字归属
文件witr --file <路径>锁/日志持有者
容器witr --container <名/ID/镜像>镜像、compose、重启策略
多目标上述任意混用可重复分段输出,退出码取最严重
只要祖先链追加 --short一行因果链
要树形图追加 --tree树 + 最多 10 个子进程
只要警告追加 --warnings体检摘要
只要环境变量追加 --env生效配置逐行列出
给脚本/AI追加 --json字段契约输出
无参数witr进入 TUI 仪表盘

退出码是这套命令体系的「第二输出」:0 干净、1 有警告、2 未找到、3 需提权、4 无效/歧义、5 内部错误、6 无法溯源(详见 C1 篇第七章,本文场景中随用随验证)。

三、场景一:端口被占用排查

问题:部署新服务时报端口被占,「8141 是谁占的、能不能杀」。

第 1 步:确认占用事实(对照传统手段):

ss -ltnp | grep 8141

实测回显:

LISTEN 0      2048         0.0.0.0:8141      0.0.0.0:*    users:(("python3",pid=744395,fd=3))

ss 给到 PID 就停了——「这个 python3 为什么在跑、是哪个服务的一部分」还得继续查。

第 2 步:一条命令问到根:

witr --port 8141

实测回显:

Target      : python3

Process     : python3 (pid 744395)
User        : root
Command     : python3 -m http.server 8141 --bind 0.0.0.0
Started     : just now (Sun 2026-10-25 09:26:12 +08:00)

Why It Exists :
  systemd (pid 1) → ? (original parent exited) → python3 (pid 744395)

Source      : session-17.scope (systemd)
Description : Session 17 of User root
Unit File   : /run/systemd/transient/session-17.scope

Working Dir : /opt/data/quzuo/tmp/witr
Git Repo    : quzuo (master)
Sockets     : 0.0.0.0:8141 (TCP | LISTENING)

Warnings    :
  • Process is listening on a public interface
  • Process is running as root
  • Process sets LD_PRELOAD (potential library injection)
  • Process sets DYLD_* variables (potential library injection): DYLD_LIBRARY_PATH

结论一次读完:占用者 pid 744395,命令行 python3 -m http.server 8141,由 root 在会话 scope(session-17.scope)里手工启动(原始 shell 已退出,因果链如实标注 ? (original parent exited)),工作目录还带着 Git 仓库信息。退出码实测 exit=1(有警告)。

第 3 步:一行因果链贴工单:

witr --port 8141 --short

实测回显:

systemd (pid 1) → ? (original parent exited) → python3 (pid 744395)

第 4 步:处置判断。来源是会话 scope 而非 *.service 单元 → 它不是任何业务服务的常驻组件,重启服务器或杀掉 PID 都能释放,且不会自动拉起。若来源显示的是某个 service/容器,处置动作就完全不同(改单元配置或改容器端口映射),这正是溯源的价值所在。

💡 技术拓展:端口由 docker-proxy、systemd socket 激活等「代持」时,ss -ltnp 只能看到代持进程(如 docker-proxy),witr 会继续回溯到背后真正的服务或容器(官方 README §8.1 Port→Container fallback),这类场景是 witr 相对 ss/lsof 的显著增量。

四、场景二:不明进程溯源

问题:巡检发现名字眼熟的 sshd,想知道监听主进程与一堆会话进程的关系,以及该不该有这么多。

第 1 步:按名字查,先看候选:

witr sshd

实测回显:

Multiple matching processes found:

[1] sshd (pid 7194)
    sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
[2] sshd (pid 25397)
    sshd: root@notty
[3] sshd (pid 88186)
    sshd: root@notty
[4] sshd (pid 214186)
    sshd: root@pts/2
[5] sshd (pid 214891)
    sshd: root@notty

Re-run with:
  witr --pid <pid>

多命中不瞎选——列清单、提示复查、退出码 4。命令行描述已能区分角色:[listener] 是监听主进程,root@notty/root@pts/2 是会话子进程。

第 2 步:复查主进程:

witr --pid 7194

实测回显:

Target      : sshd

Process     : sshd (pid 7194)
User        : root
Service     : ssh.service
Command     : sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
Started     : 2 hours ago (Sun 2026-10-25 06:46:32 +08:00)

Why It Exists :
  systemd (pid 1) → sshd (pid 7194)

Source      : ssh.service (systemd)
Description : OpenBSD Secure Shell server
Unit File   : /usr/lib/systemd/system/ssh.service

Working Dir : /
Sockets     : 0.0.0.0:22 (TCP | LISTENING)
              [::]:22 (TCP6 | LISTENING)

Warnings    :
  • Process is listening on a public interface
  • Process is running as root
  • Process is running from a suspicious working directory: /

确认:由 ssh.service(systemd)拉起、监听 22、单元文件位置明确。三条警告属 SSH 服务的正常形态,核对预期即可。

第 3 步:树形看全家:

witr --pid 7194 --tree

实测回显:

systemd (pid 1)
  └─ sshd (pid 7194)
    ├─ sshd (pid 25397)
    ├─ sshd (pid 88186)
    ├─ sshd (pid 214186)
    └─ sshd (pid 214891)

监听主进程下的 4 个会话子进程一目了然——数量与会话数对得上,无异常多出的进程。

第 4 步:深挖单个会话进程(--verbose 附加 CPU/内存/IO/文件描述符明细):

witr --pid 7194 --verbose

实测回显(节选,报告主体同上,此处仅列追加部分):

CPU (avg)   : 0.0%

Memory:
  Virtual  : 11.8 MB
  Resident : 8.2 MB
  Shared   : 7.1 MB

I/O Statistics:
  Read  : 3.4 MB (413178 ops)
  Write : 72.0 KB (5382 ops)

Open Files  : 5 of 1024 (0%)

File Descriptors: 5/1024
  0 -> /dev/null
  1 -> socket:[51663]
  2 -> socket:[51663]
  3 -> socket:[23346]
  4 -> socket:[26701]

Children of sshd (pid 7194):
  ├─ sshd (pid 25397)
  ├─ sshd (pid 88186)
  ├─ sshd (pid 214186)
  └─ sshd (pid 214891)

资源水位正常、fd 无泄漏迹象,溯源闭环。

五、场景三:容器归属确认

问题:宿主机上 containerd-shim 的一堆子进程分不清是哪个容器/镜像的;或者「这个 python 进程是哪来的」。

第 1 步:按名字精确匹配(主机侧恰好有 python3 系统进程,-x 排除干扰):

witr python -x

实测回显:

Target      : python

Process     : python (pid 849108) {forked}
User        : root
Security    : AppArmor docker-default (enforce)
Container   : docker: witr-v034-py
Image       : python:3.12-alpine
Command     : python -m http.server 8142 --bind 0.0.0.0
Started     : just now (Sun 2026-10-25 09:41:45 +08:00)

Why It Exists :
  systemd (pid 1) → ? (original parent exited) → containerd-shim-runc-v2 (pid 849084) → python (pid 849108)

Source      : docker (container)

Working Dir : /

Warnings    :
  • Process is running as root
  • Container has no healthcheck configured

一段报告报出:容器名(witr-v034-py)、镜像(python:3.12-alpine)、AppArmor 沙箱上下文、因果链经过 containerd-shim-runc-v2、来源判定为 docker (container)。

第 2 步:按容器名直查:

witr --container witr-v034-py --short

实测回显:

systemd (pid 1) → ? (original parent exited) → containerd-shim-runc-v2 (pid 849084) → python (pid 849108)

第 3 步:按镜像名查:

witr --container python:3.12-alpine --short

实测回显(同一进程命中):

systemd (pid 1) → ? (original parent exited) → containerd-shim-runc-v2 (pid 849084) → python (pid 849108)

--container 接受容器名、容器 ID(含最短 4 字符前缀)、镜像名与 compose 项目/服务名四种标识,跨 Docker、Podman、nerdctl、K8s(crictl)、containerd、Incus/LXC/LXD 及 FreeBSD jails 统一检索;有 compose 信息时报告还会给出 compose 文件路径与重启策略(README §5.7,本机演示负载无 compose 故未显示该段)。

六、场景四:文件锁与占用定位

问题:apt 报锁被占,或想知道一个日志文件正被谁写入。

第 1 步:查文件持有者(用实测环境的临时服务日志演示,机制与 dpkg 锁相同):

witr --file /opt/data/quzuo/tmp/witr/http.log --short

实测回显:

systemd (pid 1) → ? (original parent exited) → python3 (pid 744395)

第 2 步:看完整上下文判断处置(正文回显同场景一第 2 步形态:进程卡片 + 因果链 + 来源 + 警告,此处不重复)。若是 apt 场景,官方 README 的示例即 witr --file /var/lib/dpkg/lock——命中者通常是 unattended-upgr(自动升级服务),此时不应强杀,而应等它跑完或停用自动升级,溯源报告的 Source 字段直接给出了这个判断依据。

七、场景五:进程树与批量巡检

批量巡检脚本:对服务器上的关键服务逐个溯源,汇总来源与警告数(systemctl show -p MainPID 取单元主进程,--json 喂给 jq):

#!/bin/bash
# patrol.sh —— 对关键服务做溯源巡检,汇总警告数与来源
for svc in ssh.service cron.service unattended-upgrades.service; do
  unit=$(systemctl show -p MainPID --value "$svc" 2>/dev/null)
  [ -n "$unit" ] && [ "$unit" != "0" ] || continue
  witr --pid "$unit" --json 2>/dev/null | jq -r '"\(.ResolvedTarget) | source=\(.Source.Name // "unknown") | warnings=\(.Warnings | length) | \(.Warnings[0] // "clean")"'
done

实测回显(本机执行):

sshd | source=ssh.service | warnings=3 | Process is listening on a public interface
cron | source=cron.service | warnings=1 | Process is running as root
unattended-upgr | source=unattended-upgrades.service | warnings=2 | Process is running as root

三个服务的溯源、警告计数与首条警告一次输出。把 --json 换成 --short 则得到纯祖先链版巡检,接进 cron 即为轻量安全基线检查。

多目标混合查询(一次命令覆盖多类输入,分段输出):

witr --pid 7194 --port 8141

实测回显(分段头节选,正文同单目标报告):

----- [pid: 7194] -----
Process     : sshd (pid 7194)
Source      : ssh.service (systemd)
----- [port: 8141] -----
Process     : python3 (pid 744395)
Source      : session-17.scope (systemd)

八、场景六:JSON 集成与 AI 代理

JSON 输出(witr --pid 7194 --json,回显节选首尾,全文约 700 行):

{
  "Target": {
    "Type": "pid",
    "Value": "7194"
  },
  "ResolvedTarget": "sshd",
  "Process": {
    "PID": 7194,
    ...
    "Sockets": [
      {
        "Inode": "23045",
        "Port": 22,
        "Address": "0.0.0.0",
        "State": "LISTEN",
        "Protocol": "TCP"
      },
      ...
    ],
    ...
  },
  "Source": {
    "Type": "systemd",
    "Name": "ssh.service",
    "Description": "OpenBSD Secure Shell server",
    "UnitFile": "/usr/lib/systemd/system/ssh.service",
    "Details": {}
  },
  "Warnings": [
    "Process is listening on a public interface",
    "Process is running as root",
    "Process is running from a suspicious working directory: /"
  ],
  ...
}

jq 提取关键字段:

witr --pid 7194 --json | jq -r '"\(.ResolvedTarget) | source=\(.Source.Name) | warnings=\(.Warnings | length)"'

实测回显:

sshd | source=ssh.service | warnings=3

退出码分支(脚本不解析文本、只看退出码):

witr --pid 7194 --short > /dev/null 2>&1; rc=$?
case $rc in
  0) echo "All clear" ;;
  1) echo "Warnings detected" ;;
  2) echo "Process not running" ;;
  3) echo "Need elevated privileges" ;;
  4) echo "Invalid input or ambiguous match" ;;
  5) echo "Internal error" ;;
  6) echo "Cannot tell what started it" ;;
esac

实测回显(第一次,命中警告):

Warnings detected

把目标换成不存在的进程再跑,回显:

Process not running

AI 代理接入:官方 README §3 提供了给 AI 编码代理的标准话术(可直接粘进 AGENTS.md/CLAUDE.md)——端口占用用 witr --port <PORT> --json、可疑进程用 witr <name> --json、给代理的告诫是「不要从 JSON 里提取端口号去猜测配置文件,报告已含所需信息」。另有官方 Agent Skill(Claude Code:/plugin marketplace add pranshuparmar/witr)。v0.3.4 起仓库还附带 llms.txt(LLM 可读项目摘要)。代理排障时习惯串 lsof、ps、netstat、docker ps 再自行推理,witr 把这一串收敛为一条可机读命令。

九、TUI 交互实操

无参数运行 witr 即进入全屏仪表盘(等效 witr -i)。四页签能力与按键(官方 README §4,查询日期 2026-10-25):

页签内容特色
Processes实时进程列表 + 祖先树侧栏下钻进程详情
Ports监听端口 + 持有进程侧栏按 a 切 LISTEN/ALL
Containers跨 8 类运行时容器统一列表详情含镜像与重启策略
Locks系统文件锁按 a 切全量打开文件

通用操作:Tab 切页签、鼠标点选、列排序、过滤、进程下钻(环境变量/工作目录/套接字/文件上下文)、发信号(Kill/Terminate/Pause/Resume,发送前要求确认进程名)、renice(Unix);列表按 3 秒起步的自适应节奏自动刷新。

⚠️ 实测注意:本文档在非交互环境下验证了 TUI 的进入路径与守门行为——管道/脚本中运行 witr -i 会拒绝启动并返回退出码 4(实测回显见 C1 篇 8.3 节),不会挂死自动化流水线。TUI 实际按键体验请直接在服务器的交互终端上运行 witr,官方还提供无需安装的浏览器沙箱教程(pranshuparmar.github.io/witr/)。

十、排障实录

以下是本次实测过程中真实踩到并解决的三类问题,覆盖「查不准」「查不了」「测不出退出码」三种典型故障。

实录 1:短名模糊匹配误伤系统进程(查不准)

现象:想溯源一个演示负载,执行 witr python,返回的是系统进程而不是目标:

Multiple matching processes found:

[1] unattended-upgr (pid 1886)
    /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal
[2] python3 (pid 744395)
    python3 -m http.server 8141 --bind 0.0.0.0

Re-run with:
  witr --pid <pid>

原因与解法:模糊匹配按子串搜全表,python 命中了 python3 与 unattended-upgr(解释器脚本);容器里的 python 反而没进候选(匹配范围为主机进程表 + 已知容器主进程的名称)。正确姿势:确知进程全名时加 -x 精确匹配(witr python -x,实测见场景三),或直接用 --pid / --container 指定。多候选返回本身是设计行为(exit 4),不是 bug。

实录 2:内核线程溯源返回 unknown(查不了)

现象:对 pid 2 发起溯源,退出码为 6:

witr --pid 2

实测回显:

Target      : kthreadd

Process     : kthreadd (pid 2) {forked}
User        : root
Command     : kthreadd
Started     : 3 hours ago (Sun 2026-10-25 06:36:47 +08:00)

Why It Exists :
  kthreadd (pid 2)

Source      : unknown

Working Dir : /

Warnings    :
  • Process is running as root
  • No known supervisor or service manager detected
  • Process is running from a suspicious working directory: /

定位与结论:kthreadd 是内核线程母体,由内核自身启动,本就没有用户态父进程与服务单元——witr 如实返回 Source: unknown、警告注明「No known supervisor or service manager detected」,退出码 6(找到但无法溯源)。这不是缺陷而是「不猜」原则的体现:溯源边界诚实可见。排障脚本对退出码 6 的正确处理是「记录并转人工判断」,而非当作失败重试。同理,普通进程的原始父进程(shell)退出后,报告以 ? (original parent exited) 标注断点,有 systemd/容器证据时来源仍可判定(场景一实测)。

实录 3:管道悄悄吃掉退出码(测不出)

现象:用 for 循环批量验证退出码,所有目标都「exit=0」:

== pid 1 ==
systemd (pid 1)
exit=0          ← 错误:pid 1 实际应为 1(有警告)
== pid 403 ==
process 403 does not exist
exit=0          ← 错误:不存在的目标应为 2

原因与解法:命令写成了 witr --pid $p --short | head -3; echo exit=$?——$? 取到的是管道最后一个命令 head 的退出码(恒为 0),witr 的真实退出码被管道吞掉。修正:重定向到文件或先落变量再取码:

witr --pid 559 --short > /dev/null 2>&1; echo "exit=$?"

实测回显:

exit=2

⚠️ 通用教训:凡 set -o pipefail 未开启的 bash 环境里,witr ... | grep/head 后取 $? 都会失真。做退出码判断时保持「无管道直取」或显式 PIPESTATUS。

十一、命令速查表

需求命令
端口被谁占witr --port <PORT>
端口占用一行结论witr --port <PORT> --short
进程为什么在跑witr <NAME>(多匹配再 --pid 复查)
精确按名查witr <NAME> -x
看完整进程树witr --pid <PID> --tree
看资源与 fd 明细witr --pid <PID> --verbose
看生效环境变量witr --pid <PID> --env
看警告摘要witr <目标> --warnings
容器归属witr --container <名/ID/镜像>
锁/文件被谁占用witr --file <路径>
多目标一次查witr --pid <A> --port <B> --container <C>
给脚本/AIwitr <目标> --json(配 jq/退出码)
交互仪表盘witr(无参数)或 witr -i
帮助/版本witr --help / witr --version

十二、常见问题(FAQ)

Q1:witr <名字> 返回一堆候选怎么办?

多匹配是设计行为(列出候选、exit 4)。三种收窄方式:候选列表里挑 PID 后 witr --pid <PID>;确知全名加 -x;容器目标用 --container。

Q2:报告里 ? (original parent exited) 是异常吗?

不是。表示原始父进程(通常是启动它的 shell)已退出,witr 拒绝把收养者冒认启动者。有 systemd/容器/会话证据时来源照常判定;完全无证据时 Source: unknown、exit 6(见实录 2)。

Q3:管道里 echo $? 总是 0?

管道最后命令(head/grep 等)的退出码覆盖了 witr 的,见实录 3。判断退出码用无管道形式或 PIPESTATUS。

Q4:怎么批量巡检多台服务器?

单机跑 C1/D1 的巡检脚本(cron 定时),多机用现有运维通道分发同一脚本,汇总各机 --json 输出即可。witr 无守护进程、无服务端,天然适合这类「拉起即查」模式。

Q5:为什么本文的 PID 与我机器上的对不上?

PID 是实测时点快照。操作时先 witr <名字> 或 ss -ltnp 定位自己环境的目标 PID/端口,再套用相同命令组合,回显结构会一致(字段与分段相同,数值不同)。

十三、参考链接

  1. 官方 README Usage Examples:https://github.com/pranshuparmar/witr#5-usage
  2. 官方 README Output Behavior(退出码/JSON 契约):https://github.com/pranshuparmar/witr#7-output-behavior
  3. 官方浏览器交互教程(TUI 沙箱):https://pranshuparmar.github.io/witr/
  4. 本专栏认知篇:《witr v0.3.4 介绍与选型指南》(https://www.quzuo.net/document/76Q10ziexrrH)
  5. 本专栏安装篇:《Ubuntu 24.04 安装 witr v0.3.4 指南》(https://www.quzuo.net/document/SZHKmhS1Kns9)
  6. 本专栏参数篇:《witr v0.3.4 配置参数详解》(https://www.quzuo.net/document/Sj10gmxwsUta)