witr 實測:一條指令查出誰占用 Port,以及它為什麼在跑
我在 Mac 實際開了一個占用 5173 Port 的 Node 服務,再用 witr 追查程序、啟動來源與 Socket,也整理了 TUI、常用指令和公開畫面前要注意的隱私資訊。
前言
開發時遇到 Port 被占用,我以前通常就是先跑 lsof -i :5173,找到 PID,再回頭猜這個程序是從哪裡冒出來的。查得到「誰在跑」不難,麻煩的是後面那句:它到底為什麼會在這裡?
這次看到 witr,名字就是「Why is this running?」的縮寫。我先在自己的 Mac 開一個 Node 服務占住 5173,接著執行 witr --port 5173。它不只抓到 Node,連 launchd → ChatGPT → codex → zsh → node 這條啟動鏈都列了出來。

影片只在終端機帳號與私人工作路徑上做了小範圍柔焦,Port、PID、程序名稱、啟動鏈和一般網路資訊都保留。第一張圖右下角原本還有啟動參數的 PIN 與 Key,所以只有那兩個值一定得遮;witr 真的會挖出不少本機資訊,公開截圖前還是要多看一次。
它和 lsof、ps 差在哪裡?
ps、top、lsof 和系統監視工具都能告訴我目前有哪些程序、誰占用了 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 的話,直接一行就能裝:
brew install witr
安裝完成先確認版本:
witr --version
Windows 可以用 Winget,Linux 也有 APT、安裝腳本和其他套件管理器。官方 README 列的方法很多,我覺得沒必要全部抄一遍;到 Releases 或 README 選自己原本就在用的安裝方式就好。
我先故意占住 5173 Port
為了讓結果比較好懂,我先在本機啟動一個很小的 Node HTTP Server:
node -e 'require("http").createServer((req,res)=>{res.end("witr demo\n")}).listen(5173,"127.0.0.1")'
這個服務會一直占用 127.0.0.1:5173。接著另外開一個終端機執行:
witr --port 5173
我的結果直接顯示 Target 是 node,Socket 正在監聽 127.0.0.1:5173,來源是 zsh。最有意思的是啟動鏈一路往上追到 launchd,中間經過 ChatGPT、Codex 和 zsh,最後才是實際占用 Port 的 Node 程序。

這剛好就是我想看的東西。如果只是想關掉 Port,拿到 PID 就夠了;但碰到背景服務、開發伺服器或容器自己重啟,知道誰把它叫起來,才比較不會殺完一次又再出現。
測完記得回到原本執行 Node 的終端機按 Control + C。不要為了清 Port 直接亂殺不認識的系統程序。
不加參數就是完整 TUI
直接執行 witr,會打開終端機互動介面。上方分成四頁:
Processes:程序清單、CPU、記憶體和啟動鏈。Ports:目前監聽中的 Port 與擁有它的程序。Containers:Docker、Podman、Kubernetes 等容器資訊。Locks:檔案鎖與開啟中的檔案。
可以用方向鍵移動、Enter 看詳細資料,也能按 / 搜尋。我這台 Mac 當時一次列出五百多個程序,直接滑其實有點多;如果已經知道 Port 或 PID,我還是比較喜歡從精準指令開始。TUI 適合不知道問題在哪裡,想先掃一遍整台機器的時候。

Unix 系統還能在介面裡對程序送出 Kill、Terminate、Pause、Resume 或調整優先權。這些動作真的會影響程序,不確定用途就先查,不要把 TUI 當工作管理員一路結束。
幾個我覺得最實用的查法
查程序名稱:
witr node
只看一行啟動鏈:
witr --port 5173 --short
用樹狀方式看父子程序:
witr --pid 12345 --tree
需要交給其他程式處理時,可以輸出 JSON:
witr --port 5173 --json
它也能從檔案與容器往回查:
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,我會先跑:
witr --port 5173
至少不用停在「Node 占用了 Port」這一步,後面的啟動來源也一起有答案。

