Mac App 無法解除安裝:七種常見原因與排查方法
macOS 上解除安裝失敗不是一種故障,是好幾種。Finder 可能根本不讓你搬走那個套件,也可能搬走成功了,下次登入 App 又回來,還可能套件消失了,當初對應的輔助程式照樣在跑。機制不一樣,修法也不一樣,所以要從這台 Mac 到底做了什麼開始查。一般流程見徹底解除安裝,下面這些是在那條路已經走不通之後。
從症狀開始
- 「因為項目正在使用中,所以無法移到垃圾桶。」 有東西在跑,而且常常不是你剛關掉的那個 App。
- 明明刪掉了,它又回來。 先分清楚是尚未移除的輔助程式重新啟動,還是 App 被管理軟體或套件管理器重新安裝。
- Finder 要管理者密碼。 某個
.pkg把套件的所有者留成了 root,這很正常。 - 按了什麼都沒發生,沒有對話框也沒有錯誤。 「App 管理」權限是可能原因之一,還要確認操作結果和檔案權限。
- 以 root 執行
rm卻回 Operation not permitted。 可能涉及系統保護或其他限制,不能只憑這句錯誤判定為 SIP。 - 變成灰色,或者在公司的 Mac 上一再出現。 它屬於某份設定描述檔或 MDM。
1. App 或它的某個輔助程式還在跑
套件裡有檔案開著,Finder 就不肯搬它,而關掉你看得見的那個行程,並不等於停掉這個 App 拉起來的全部東西。「活動監視器」列的是每一個行程,不只是有視窗的那些。搜廠商名稱,然後把整組結果讀完,一款叫 Foo 的 App 常常還帶著 Foo Helper 和一個 FooUpdater,還有一個登入項目套件,顯示名稱跟它八竿子打不著。蘋果選單 › 強制結束那個視窗代替不了它,因為那裡只列有介面的應用程式。
pgrep -fl -i foo
lsof +D /Applications/Foo.app 2>/dev/null | awk '{print $1, $2}' | sort -u
pgrep -fl 比對的是完整命令列,所以就算輔助程式的行程名稱裡沒有廠商名稱,只要可執行檔路徑裡有,它照樣抓得到。lsof +D 列出每一個握著套件內某個檔案的行程。
強制結束母行程不一定停得掉輔助程式。輔助程式是獨立的行程、有自己的 PID,母行程若沒有在結束時把它收掉,殺掉母行程只是讓它變成孤兒,照樣在跑。而如果那個輔助程式歸 launchd 管,殺掉誰都只是通知 launchd 再啟動一次,那是下一節的事。
「快速查看」的預覽或 Spotlight 建索引也可能握著套件裡的檔案,重新開機兩個都會清掉。訊息又出現的話,Apple 的移除 App 說明建議進安全模式。
2. 某個 launchd agent 或 daemon 把它拉回來
這就是「我刪掉了它又回來」那一種,也是最常被誤判的一種,大家去「活動監視器」看一眼,什麼都沒有,就斷定 App 沒在跑。按需啟動的意思就是,那個「什麼都沒有」不構成證據。
launchd 監管背景工作。一筆工作就是一份 property list,裡面有 Label、一個程式,以及執行它的條件。RunAtLoad 在工作載入時啟動它,KeepAlive 在它結束時把它拉回來。兩個都沒有的工作照樣會回來,MachServices、Sockets、WatchPaths、QueueDirectories 和 StartInterval 都會讓 launchd 在有東西來要的那一刻啟動這個行程,所以有些輔助程式閒置十秒就結束,靠自己的 Mach 服務再起來,你每次去看,它都剛好不在。
定義放在三個地方。~/Library/LaunchAgents 只屬於當前使用者,在登入工作階段裡;/Library/LaunchAgents 在每個使用者登入時執行;/Library/LaunchDaemons 則在任何人登入之前,以 root 身分在整個系統範圍執行,這就是為什麼手動解除安裝殺得掉 agent,卻常常殺不掉 daemon。/System/Library/Launch* 是 Apple 的,受保護。
launchctl list | grep -i foo
launchctl print-disabled gui/$(id -u) | grep -i foo
grep -l -i foo ~/Library/LaunchAgents/*.plist /Library/LaunchAgents/*.plist \
/Library/LaunchDaemons/*.plist 2>/dev/null
在 launchctl list 裡,帶 PID 的標籤是現在正在跑,帶一個橫線的是已載入、在等觸發條件;print-disabled 讀的是持久的停用狀態資料庫,跟「plist 存不存在」是兩回事。用 plutil -p <path> 讀候選的那一份,確認 Program 或 ProgramArguments 指的就是你要移除的那個 App,因為廠商不見得會用裡面的標籤替檔案命名。
順序很重要
先把工作停掉並停用,再刪 plist,最後才移除 App。
launchctl bootout gui/$(id -u)/com.vendor.foo.helper
sudo launchctl bootout system/com.vendor.foo.daemon
launchctl disable gui/$(id -u)/com.vendor.foo.helper
工作還載入著就刪 plist,launchd 手上就留著一個磁碟上已經沒有定義的服務,它一路跑到下次重新開機,還可能在停用狀態資料庫裡留下一筆過期紀錄。先移除 App 更糟,那筆工作會對著一個不存在的可執行檔一再重生,反覆失敗成一個迴圈,「東西明明沒了,登入項目裡卻還列著」就是這麼來的。
macOS 13 以後,App 可以透過 SMAppService 註冊背景元件,輔助程式及其定義可能隨 App 套件提供,不一定在上述三個資料夾另放一份 plist。註冊狀態由系統管理,並不是套件裡的那份檔案,可到系統設定 › 一般 › 登入項目與延伸功能查看及控制(見關閉開機啟動項目)。
3. 它是受 SIP 保護的系統 App
系統完整性保護是核心層級的政策,不是一個權限位元。Apple 的說法是它用核心權限把關鍵系統檔案鎖成不可寫,而且適用於「系統上執行的每一個行程,不論該行程是否在沙盒中執行,或是否具有管理者權限」(Apple 平台安全性)。從 Big Sur 起,系統內容還放在一個獨立、以密碼學封印的磁碟區上。csrutil status 印出保護是不是開著,而 Apple 也講明「你無法移除 Mac 必需的 App」,這一組包括「郵件」、「音樂」、「書籍」、「備忘錄」、「Podcast」、「地圖」、「新聞」和「股市」。
為了刪掉一個套件而關掉 SIP,這筆交易不划算。你得開機進復原模式,改掉一條全機範圍的安全性政策,Intel Mac 上,Apple 提到關掉它會讓那顆實體儲存裝置上的每一個分割區都失去保護,Apple 晶片的 Mac 則會離開「完全安全性」。好處還留不住,下一次 macOS 更新會整個換掉系統磁碟區,而且一點可用空間都回收不到。
更好的做法是把圖示從 Dock 拖出去,到系統設定 › 一般 › 登入項目與延伸功能把它移掉,它要是一直搶著開檔案,就用顯示簡介 › 打開檔案的應用程式 › 全部更改換掉。
4. 它是 MDM 或設定描述檔裝上去的
受管理的 Mac 上,管理伺服器可能按排程重新安裝 App,描述檔也可以設成不可移除。症狀可能是移除後又被安裝、控制項變成灰色,或在 launchd 找不到對應工作,因為重新安裝是由遠端發起。先看系統設定 › 一般 › 裝置管理,沒有看到這一節也不能只憑介面排除管理狀態,還要核對下面的註冊資訊,公司或學校的 Mac 可向 IT 確認。
profiles status -type enrollment
sudo profiles list
第一條回報的是自動裝置註冊的狀態,以及有沒有經過使用者核准;第二條列出已安裝的描述檔,需要 root。然後去問 IT。Apple 的建議是,移不掉的描述檔就去問當初提供它的人,還提醒移除一份描述檔,會把它設定過的東西全部刪掉,所以一份帶著你郵件帳號的描述檔,會把帳號一起帶走。
5. 所有權,以及那個會靜默失敗的權限
Finder 要管理者密碼。 很平常。.pkg 安裝程式是以 root 執行的,留下的套件所有者就是 root,所以搬動它需要驗證身分。用 ls -ld /Applications/Foo.app 確認一下。順帶把安裝收據弄清楚,pkgutil --files <id> 列出一個套件放過哪些路徑,sudo pkgutil --forget <id> 把那張收據從 /private/var/db/receipts 拿掉,但一個檔案都不會刪。
什麼都沒發生。 沒有提示、沒有錯誤,App 還在 /Applications 裡,而你用的那個工具報了一個含糊的失敗,或者乾脆改去做別的。macOS 14 以後,這通常是「App 管理」,Apple 對它的描述是「允許 App 更新或刪除 Mac 上的其他 App」。判斷方式很簡單,這條路徑在 POSIX 層看得出可寫,寫入卻還是失敗:
test -w /Applications/Foo.app && echo "posix says yes"
這一行只檢查 App 路徑是否可寫,不能證明可以刪除它,刪除還取決於上層資料夾的寫入和搜尋權限等條件。如果權限檢查無法解釋失敗,再到系統設定 › 隱私權與安全性 › App 管理確認執行移除的 App 是否獲准,調整後重新啟動它。
底層:三種長得一樣的拒絕
POSIX 權限是第一道門,套件的所有者是 root,你不是,sudo 就解得開,因為這道檢查只問你是哪個使用者。TCC,也就是系統設定背後那層隱私權機制,在 POSIX 通過之後才做決定。「App 管理」是 TCC 針對單一操作設的一道門,那個操作就是修改或刪除另一個 App 的套件。你是管理者不算數,sudo 也不算數,因為它認的是發出請求的那個程式,不是使用者,所以它的拒絕才會變成一個不跳提示的籠統錯誤。
從表面現象就能分開這三種情況。Finder 跳出密碼框,是一般的所有權問題,輸入密碼驗證就能解決。「App 管理」擋下刪除時不會跳出密碼框,macOS 通常會在通知中心送出一則橫幅,說這個 App 被阻止修改 Mac 上的 App,這時要做的是到隱私權設定打開開關、再重新啟動那個 App,而不是輸入密碼。以 root 身分得到 Operation not permitted,代表擋路的是輸入密碼也解不開的保護。「App 管理」就是其中之一,發出命令的終端機沒有取得這項授權時,連 sudo 也會得到這個錯誤。其他保護機制也會回報同樣的錯誤,所以先按路徑和發出請求的 App 排查,再考慮是不是 SIP。
SIP 也會限制 root,但錯誤訊息不能直接對應成單一保護機制。EACCES(Permission denied)可能來自上層資料夾的寫入或搜尋權限,EPERM(Operation not permitted)也不只由 SIP 引起,具體條件見 Apple 的檔案刪除說明。先確認失敗的路徑和權限,不要只看到錯誤就提高權限或關閉系統保護。
6. 系統延伸功能或網路過濾器還開著
資安軟體、VPN 用戶端和虛擬化產品都會安裝系統延伸功能。App 套件只是註冊那個延伸功能的,它自己不是延伸功能。延伸功能還開著就把容器刪掉,那筆登記就沒有所有者了,Mac 會繼續把流量交給你以為已經移除的軟體去過濾,就是這麼來的。
systemextensionsctl list
輸出裡有 team identifier、bundle identifier,以及像 [activated enabled] 這樣的狀態。停用這件事屬於承載它的那個 App,而且常常需要重新開機,所以先跑原廠的解除安裝程式。怎麼解除安裝 Mac 上的防毒軟體講的就是這一類軟體的拆除順序。
7. 它來自 App Store,或者 Homebrew
Mac App Store 的 App。 刪掉套件不等於取消購買。App Store 的「自動下載你在其他 Mac 電腦和裝置上從 App Store 購買的 App」針對的是其他裝置上的購買行為,不代表另一台 Mac 仍裝著它,本機就一定會重新安裝。遇到自動下載時,可到 App Store › 設定確認這一項。
Homebrew 的 cask。 當初是用 brew install --cask foo 裝的,把 App 丟進垃圾桶之後,Homebrew 仍然認為它裝著。brew list --cask 照樣列得出那個名稱,而下一次 brew upgrade 就可能把你剛刪掉的 App 裝回來,這也是一種貨真價實的「它又回來了」,只是跟 launchd 無關。改用 brew uninstall --cask foo。Homebrew 的手冊把 --zap 描述成移除「與一個 cask 相關的所有檔案」,也警告它「可能移除多個應用程式之間共用的檔案」,所以那個參數要想清楚再用。
刪成功之後,還留下什麼
「登入項目與延伸功能」裡還列著它。 要嘛是一筆「登入時打開」的項目,指向一條已經不存在的路徑,要嘛是背景工作管理裡的一筆服務紀錄,程式早就不在了。到系統設定 › 一般 › 登入項目與延伸功能用減號把它移掉,macOS 通常在重新開機之後也會自己清掉。
開機時冒出一個選單列圖示。 還有東西在載入一個執行檔,通常是一個你沒移掉的 launch agent,或者安裝程式複製到 ~/Library/Application Support/<vendor> 裡的輔助程式。
特權輔助程式還活著。 需要 root 的軟體會裝到 /Library/PrivilegedHelperTools/<label>,配一份對應的 /Library/LaunchDaemons/<label>.plist。兩個的所有者都是 root,也都在套件外面,所以把 App 丟進垃圾桶,這兩個都碰不到。
ls -la ~/Library/LaunchAgents /Library/LaunchAgents /Library/LaunchDaemons \
/Library/PrivilegedHelperTools 2>/dev/null
資料那一側見解除安裝之後的殘留檔案。
用 Mole 做這套診斷
手動排查需要查看「活動監視器」、三個 launchd 資料夾、「登入項目與延伸功能」、「隱私權與安全性」,以及 Finder。Mole 把辨識到的同一款 App 的相關項目放在同一頁,而且掃描一直免費,可以先用來查看,不必立即購買或刪除。
打開 Mole 的「軟體」分頁,裡面有三段,已安裝的 App 清單、可用的更新,以及開機啟動項目。最後那一段,就是這篇文章要你手動拼出來的那份 launchd 和登入項目清單,所以「它會回來」的成因,在你刪之前就看得見,不是刪完才看見。
選中一款 App,Mole 會解析它的 bundle identifier,再找出這個身分名下的東西,Application Support、Caches、Preferences、Containers、HTTPStorages,還有 launch agent 和 daemon,條件是那些 plist 真的引用到這個 App,不是只跟它的名字撞了一個詞。每個候選都帶著路徑、大小,還有把它掛上來的證據,信心不足的一律預設不勾。
按下確認,跑的就是這篇文章建議的那個順序,而且是程式強制執行,不靠人記得。Mole 會關掉這個 App 和它套件裡巢狀的輔助程式,把它的登入項目輔助程式踢出去,在刪 plist 之前先用 launchctl 卸下已核准的啟動項目,並在真正刪除的那一刻把每條路徑再驗一次。刪除走垃圾桶,弄錯了是從垃圾桶拖回來。
Mole 會依路徑回報略過與失敗的項目,幫助判斷是「App 管理」權限、管理者認證,還是其他原因阻止了刪除,不能只看成功總數。掃描與清理在本機執行,清理操作寫入 ~/Library/Logs/mole/operations.log,記錄不代表檔案目前仍保留在垃圾桶。終端機裡,Mole CLI 免費且開源,mo uninstall 支援 --dry-run,可以先讀路徑清單。
有三個成因 Mole 贏不了,也不去硬碰。它不會關掉 SIP,也不會從封印的磁碟區裡移除 Apple 內建的 App。受管理的 Mac 上,設定描述檔會把軟體裝回來,這一點它蓋不過。遇到資安代理程式、VPN 用戶端和虛擬化產品,它取代不了那個知道拆除順序的原廠解除安裝程式。「App 管理」被拒的時候,Mole 回報這次失敗,不會繞過去提權。
常見問題
為什麼 App 刪掉之後又回來?
先分清楚重新出現的是輔助程序還是 App 套件。launchd 能重新啟動尚未移除的輔助程式,但不會只憑工作定義還原已刪除的 App 套件。App 被重新安裝時,再檢查 Homebrew 紀錄是否仍在、有沒有執行過 brew uninstall --cask,以及 MDM 部署和 App Store 的其他裝置購買自動下載設定。
關掉 SIP 就可以刪掉 Apple 內建的 App 嗎?
技術上可以,但這筆交易不划算,你要從復原模式做一次全機範圍的安全性降級,而換來的那個套件,下一次 macOS 更新換掉封印的系統磁碟區時就會回來。把它從 Dock 和登入項目裡移掉就好。
Finder 刪 App 時要密碼,是出了什麼問題嗎?
不一定有問題。例如,由 .pkg 安裝程式以 root 身分安裝的 App,移動時可能需要驗證身分。如果沒有提示、沒有錯誤,卻也沒有刪除,「App 管理」權限遭拒是可能原因之一,還要配合實際錯誤、資料夾權限與程序狀態判斷。「App 管理」認的是提出請求的程式,不能靠 sudo 繞過。
延伸閱讀
- 如何徹底解除安裝 Mac 上的 App,講一般流程,以及它會逐層審過的資源庫。
- 怎麼解除安裝 Mac 上的防毒軟體,講帶系統延伸功能的軟體,那一類的重點整個就在拆除順序。
- 怎麼關閉 Mac 的開機啟動項目,講沒有東西再跟你作對之後,launchd 和登入項目那一側該怎麼收。