幕後花絮
AI 幫我關個開發伺服器,順手把我的瀏覽器全砍了——而且兩次
文/POMODIARY 團隊 · · 閱讀約 2 分鐘
9 月 14 日,我的 Chrome 所有分頁同時當掉。
Claude 桌面版、ChatGPT、Obsidian 也一樣,同一秒全部變成白畫面。
我盯著螢幕愣了幾秒,才意識到:這個場面我看過。一週前,9 月 7 日,一模一樣。
兇手是一行看起來很謹慎的指令
兩次的起因都一樣:我請 AI 幫我把本機的 Next.js 開發伺服器關掉。它執行了這麼一行:
pkill -f "next-server" -U "$(id -u)"
單看這行指令,你甚至會覺得它挺周到的:-f 依完整命令列比對 next-server,-U 再限定「只砍目前使用者的程序」,雙重保險。
問題就出在這個「雙重保險」上。
macOS 的 pkill 不會幫你重排參數
Linux 上的 pkill 用的是 GNU getopt,參數順序隨你寫,它會自己整理好:選項歸選項,pattern 歸 pattern。
macOS 上的 pkill 用的是 BSD getopt:遇到第一個不是選項的參數,就停止解析,後面的東西一律當成 pattern。而 pkill 又支援同時傳入多個 pattern,符合其中任何一個就算數。
所以這行指令在 macOS 眼中其實是三個 pattern:
- next-server
- -U
- 501(我的使用者 ID)
再加上 -f 是依完整命令列比對,它真正的意思變成:砍掉所有命令列裡含有 -U 或 501 的程序。
Chrome、Claude、ChatGPT、Obsidian 這類以 Chromium / Electron 為基礎的應用程式,每個分頁、每個視窗背後都是一堆 Helper 程序,啟動參數又長又雜,非常容易被比對到。一行指令下去,全軍覆沒。
本來想多加一層保險,結果這層保險本身成了炸彈。
我後來怎麼改
第一,關開發伺服器要依連接埠找,不要靠名稱猜。
lsof -ti :3000 -sTCP:LISTEN | xargs kill
誰在監聽這個連接埠就砍誰,不會誤傷。
第二,非用 pkill 不可的話,選項全部寫在 pattern 前面,pattern 要具體到連接埠或路徑。 動手之前,先用 pgrep 帶同樣的參數預覽一遍:
pgrep -fl "next start -p 3011"
pkill -f "next start -p 3011"
先看會比對到誰,再動手。
第三,也是我認為最重要的一點:把它寫成 hook,不要只寫成規則。
規則是請求,hook 才是保證
同一個坑我踩了兩次。現在我給 Claude Code 的全域規則檔 CLAUDE.md 裡,專門有一節講怎麼停止程序,兩次出事的日期 9 月 7 日和 9 月 14 日都記在裡面。
第二次之後,我不再指望「記住」這件事了。我用 Claude Code 的 PreToolUse hook 寫了一支兩百多行的 Python 腳本:AI 每次要執行 Bash 指令之前,先經過一輪檢查,只要發現 pkill / pgrep 把選項寫在 pattern 後面,就直接擋下來,不讓它執行。
這件事讓我想通了一個差別:
寫在提示詞、規則檔裡的東西,對 AI 來說是「請求」。它大多會遵守,但上下文一長、任務一急,就有可能漏掉。
寫在 hook 裡的東西則是「保證」。它不取決於 AI 記不記得、懂不懂,指令根本到不了 shell。
管理團隊其實也是同樣的道理:重要的事不要靠「大家注意一下」,要靠流程把關。能用 CI 擋的就別靠 code review,能用權限擋的就別靠自律。
現在讓 AI 直接在自己的電腦上執行指令,已經是家常便飯。它犯的錯不見得比人更離譜,但它犯錯的速度比人快得多。給它訂規矩的同時,也記得裝上護欄。
至於我,現在每次看到 pkill,都會反射性地先看一眼參數順序。
兩次全軍覆沒的分頁,換來這麼一個肌肉記憶,也不算太虧。
延伸閱讀
- 我把小時候看螞蟻搬蟲子的回憶,做成了 20 關遊戲
Crumb Colony 第六章「草叢野味」:20 隻螞蟻真的會搬的蟲子。這一章的關鍵盒子常埋在深處,光靠推理推不出來,但每一關都保證不用道具也能過關。
- 一個 10 萬+ 下載的老 App,沉寂兩年,翻新 13 天後又開始賺錢了
番茄打卡在 Google Play 有 10 萬+ 下載,卻一兩年沒有像樣的更新。13 天合併 53 個 PR 翻新之後,9 月底起又陸續有了訂單。
- 我做了一款小遊戲:看一群螞蟻,把像素畫一口一口搬回家
Crumb Colony 是一款網頁像素解謎小遊戲:放出同色螞蟻,看牠們把像素畫一口一口搬回蟻穴。20 道像素美食,每一關都確認有解。
