跳至主要內容

witr 實測:一條指令查出誰占用 Port,以及它為什麼在跑

我在 Mac 實際開了一個占用 5173 Port 的 Node 服務,再用 witr 追查程序、啟動來源與 Socket,也整理了 TUI、常用指令和公開畫面前要注意的隱私資訊。

6 min read/ Easy

前言

開發時遇到 Port 被占用,我以前通常就是先跑 lsof -i :5173,找到 PID,再回頭猜這個程序是從哪裡冒出來的。查得到「誰在跑」不難,麻煩的是後面那句:它到底為什麼會在這裡?

這次看到 witr,名字就是「Why is this running?」的縮寫。我先在自己的 Mac 開一個 Node 服務占住 5173,接著執行 witr --port 5173。它不只抓到 Node,連 launchd → ChatGPT → codex → zsh → node 這條啟動鏈都列了出來。

witr Processes TUI 顯示程序清單與啟動鏈,右下角敏感參數值已模糊

影片只在終端機帳號與私人工作路徑上做了小範圍柔焦,Port、PID、程序名稱、啟動鏈和一般網路資訊都保留。第一張圖右下角原本還有啟動參數的 PIN 與 Key,所以只有那兩個值一定得遮;witr 真的會挖出不少本機資訊,公開截圖前還是要多看一次。

它和 lsof、ps 差在哪裡?

pstoplsof 和系統監視工具都能告訴我目前有哪些程序、誰占用了 Port。witr 的重點不是把這些工具再做一次,而是把散在不同地方的資訊串成原因。

查一個 Port 時,它會先找到對應 PID,再往上追父程序、啟動服務、Shell 或容器。最後整理出程序名稱、使用者、完整指令、啟動時間、工作目錄、Git 儲存庫和 Socket。看到 Why It Exists 那一行,就不用自己拿著 PID 到處對答案。

它目前是 Apache-2.0 授權的開源專案,支援 macOS、Linux、Windows 與 FreeBSD。我這次在 Mac 使用的是 v0.3.3,也是官方 Releases 目前列出的最新版。

Mac 安裝最簡單

完整版本與其他平台的安裝檔都在 witr 官方 Releases。Mac 有 Homebrew 的話,直接一行就能裝:

bash
brew install witr

安裝完成先確認版本:

bash
witr --version

Windows 可以用 Winget,Linux 也有 APT、安裝腳本和其他套件管理器。官方 README 列的方法很多,我覺得沒必要全部抄一遍;到 Releases 或 README 選自己原本就在用的安裝方式就好。

我先故意占住 5173 Port

為了讓結果比較好懂,我先在本機啟動一個很小的 Node HTTP Server:

bash
node -e 'require("http").createServer((req,res)=>{res.end("witr demo\n")}).listen(5173,"127.0.0.1")'

這個服務會一直占用 127.0.0.1:5173。接著另外開一個終端機執行:

bash
witr --port 5173

我的結果直接顯示 Target 是 node,Socket 正在監聽 127.0.0.1:5173,來源是 zsh。最有意思的是啟動鏈一路往上追到 launchd,中間經過 ChatGPT、Codex 和 zsh,最後才是實際占用 Port 的 Node 程序。

使用 witr --port 5173 找到 Node 程序、啟動鏈與監聽中的 Socket

這剛好就是我想看的東西。如果只是想關掉 Port,拿到 PID 就夠了;但碰到背景服務、開發伺服器或容器自己重啟,知道誰把它叫起來,才比較不會殺完一次又再出現。

測完記得回到原本執行 Node 的終端機按 Control + C。不要為了清 Port 直接亂殺不認識的系統程序。

不加參數就是完整 TUI

直接執行 witr,會打開終端機互動介面。上方分成四頁:

  1. Processes:程序清單、CPU、記憶體和啟動鏈。
  2. Ports:目前監聽中的 Port 與擁有它的程序。
  3. Containers:Docker、Podman、Kubernetes 等容器資訊。
  4. Locks:檔案鎖與開啟中的檔案。

可以用方向鍵移動、Enter 看詳細資料,也能按 / 搜尋。我這台 Mac 當時一次列出五百多個程序,直接滑其實有點多;如果已經知道 Port 或 PID,我還是比較喜歡從精準指令開始。TUI 適合不知道問題在哪裡,想先掃一遍整台機器的時候。

witr 程序詳細頁顯示啟動來源、Socket、資源用量與環境變數

Unix 系統還能在介面裡對程序送出 Kill、Terminate、Pause、Resume 或調整優先權。這些動作真的會影響程序,不確定用途就先查,不要把 TUI 當工作管理員一路結束。

幾個我覺得最實用的查法

查程序名稱:

bash
witr node

只看一行啟動鏈:

bash
witr --port 5173 --short

用樹狀方式看父子程序:

bash
witr --pid 12345 --tree

需要交給其他程式處理時,可以輸出 JSON:

bash
witr --port 5173 --json

它也能從檔案與容器往回查:

bash
witr --file /path/to/locked-file
witr --container redis

同一個查詢後面還能加上 --verbose--env--warnings。平常我不會一開始就把環境變數全印出來,先看一般輸出,缺什麼再加參數,畫面比較不會被資訊塞滿。

截圖或貼 Log 前,先把敏感資訊遮掉

這次整理素材時,我原本也覺得只是程序清單,應該沒什麼好藏。真的放大檢查才發現,畫面裡有本機帳號、完整磁碟路徑、Git 分支、區網 IP、遠端連線位址和環境變數。某些程序的完整 Command 甚至可能帶著 Token、PIN 或其他啟動參數。

這次我沒有把整個使用者欄位或詳細面板蓋掉,因為那樣連工具在做什麼都看不到。真正一定要處理的是第一張圖右下角的 PIN 與 Key;影片則只柔焦終端機帳號、工作目錄與 Git 路徑,其他對理解功能有幫助的內容都保留。

witr 是拿來調查自己電腦的工具,但它輸出的內容不等於適合直接貼到網路。尤其使用 --env--verbose,或打開 TUI 詳細頁時,公開前最好把整張圖重新看過一次。

我會在什麼時候用它?

如果只是偶爾查一次 Port,lsof 當然還是很快,我不會為了每個 PID 都硬換工具。witr 比較適合我已經找到程序,卻還想知道它是從 Shell、服務、容器還是其他工具啟動的時候。

下次再看到 EADDRINUSE,我會先跑:

bash
witr --port 5173

至少不用停在「Node 占用了 Port」這一步,後面的啟動來源也一起有答案。