关掉 Mac 升级弹窗和红色角标

00:00 引言 00:31 AI 找真凶 01:03 关弹窗 01:37 关红色角标 02:19 一个坑 02:53 还原修改

相关代码:本文即完整命令清单,无需另外下载

本节解决的两件事

  • 弹窗:屏幕上飘出来的通知横幅,发送者是一个叫 SoftwareUpdateNotificationManager 的后台服务。
  • 红色角标:系统设置(以及 Dock 上的齿轮图标)右上角那个红点,来源是「已检测到可用更新」这条缓存记录。

去除弹窗 ≠ 清掉红点, 两段各做各的。


最简版

不想看原理的话,照抄这几条就行。

关弹窗

# 禁用发弹窗的服务(会看到一句 SIP 报错,先别管,重点看第三条验收)
sudo launchctl disable gui/$(id -u)/com.apple.SoftwareUpdateNotificationManager
 
# 结束还在跑的那个进程
pkill -9 -f SoftwareUpdateNotificationManager
 
# 必须验收:输出末尾是 disabled 才算成功,一行都没有就是没写进去,重跑
launchctl print-disabled gui/$(id -u) | grep SoftwareUpdateNotification

2026-09 补充

按照最简版命令做完,一段时间后又出现了一次弹窗, 解决方案见补充章节

关红标

# 挡住更新检查的域名(没挂代理时靠这两条)
# swscan 管常规更新目录,gdmf 管大版本升级信息,只挡一个照样会被发现新系统
sudo sh -c 'echo "0.0.0.0 swscan.apple.com" >> /etc/hosts'
sudo sh -c 'echo "0.0.0.0 gdmf.apple.com" >> /etc/hosts'
 
# 不再自动检查更新(这个键不持久,见下面的提示)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -bool false
 
# 清掉"有更新可用"的历史缓存
sudo sh -c 'defaults delete /Library/Preferences/com.apple.SoftwareUpdate RecommendedUpdates; killall softwareupdated 2>/dev/null; true'
 
# 清掉图标上当前那个红点
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
osascript -e 'quit app "System Settings"'; killall Dock

挂了代理的话还要加两条

上面那两条 hosts 可能被代理绕过。在代理软件的规则里同样加上这两个域名,如 Shadowrocket 放 [Rule] 段顶部:

DOMAIN,swscan.apple.com,REJECT
DOMAIN,gdmf.apple.com,REJECT

恢复提醒和红标

# 恢复弹窗
sudo launchctl enable gui/$(id -u)/com.apple.SoftwareUpdateNotificationManager
 
# 恢复更新检查(记得同时删掉代理里那行规则)
sudo sed -i '' -E '/(swscan|gdmf)\.apple\.com/d' /etc/hosts
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -bool true

屏蔽期间的另一个影响

手动检查更新也会失败;走同一通道的 Xcode Command Line Tools 安装/更新、Rosetta 2 安装同样受影响。不影响 App Store 应用更新、iCloud、推送和日常浏览。


一、升级弹窗

初始方案

Mac 时不时弹一次「升级到 macOS Tahoe」。不管它,它就一直挂在屏幕上挡着你正在看的东西,一天弹好几次,专注全被打断。

在系统设置里关闭自动更新,照着网上的方法照着跑了,第二天它照样弹。

网上的命令为什么没用

几乎所有教程都会给这一条:

sudo softwareupdate --ignore "macOS Tahoe"

它不是一直没用,而是从 macOS 12.3 开始,只对受管理的设备(公司统一管理、带 MDM 描述文件的电脑)有效。个人机器上跑完不会报错,也不会有任何变化——这是最容易误判「我是不是哪里做错了」的地方。

但它现在还挂在搜索结果里,一篇抄一篇。

我们搜到的是别人机器上的答案,而且可能已经过期。 AI在这里最有用的是,它可以根据我们的电脑实时情况来进行排查。


找出发弹窗的那个服务

通过设置关闭自动更新等方法不能阻止弹窗,转换思路,去找发弹窗的那个服务 通过名字去后台服务里搜:

ls /System/Library/LaunchAgents/ | grep -i softwareupdate

会找到 com.apple.SoftwareUpdateNotificationManager.plist。打开它的配置核对一遍:

plutil -p /System/Library/LaunchAgents/com.apple.SoftwareUpdateNotificationManager.plist

输出里 LaunchEvents 那段说明了它什么时候会被叫醒(节选):

"UpdatesAvailable"    => "com.apple.SoftwareUpdate.updatesAvailable"
"BadgingCountChanged" => "com.apple.SoftwareUpdate.BadgingCountChanged"

对上了:检测到有可用更新,就把它叫起来发通知。发弹窗的人抓到了。

第二条 BadgingCountChanged(角标数变化)先记住——那是后面红点那条线的线索。

可迁移的排查思路

这个路子不只对升级提醒有效:去 /System/Library/LaunchAgents/ 里按关键词反查服务 → 用 plutil -p 看它的唤醒事件。任何一条「设置里关不掉」的系统通知,都可以照这个思路往下挖。


禁用对应的弹窗服务

sudo launchctl disable gui/$(id -u)/com.apple.SoftwareUpdateNotificationManager

输入管理员密码后,大概率会看到这句报错:

Boot-out failed: 150: Operation not permitted while System Integrity Protection is engaged

这个报错不能想当然地忽略

报错本身说的是紧跟着那一步「把正在跑的进程卸载掉」,系统自带服务不允许这样卸载,这部分确实不用管,手动结束残留进程即可。

但不能据此认为前面的 disable 一定写进去了。本机 2026-09-10 复查时,禁用列表里根本找不到这条服务,而同一份列表里 Google、Microsoft 的更新代理都好好地写着 disabled——写入机制没毛病,就是这一条没落上。所以跑完必须做下面的验收,没看到 disabled 就是没生效,重跑一次。

更进一步(2026-09-25):就算写进去了也不够。 列表里显示 disabled,这个服务照样会被系统事件叫醒、照样发弹窗,详见文末补充。

pkill -9 -f SoftwareUpdateNotificationManager

验收:

launchctl print-disabled gui/$(id -u) | grep SoftwareUpdateNotification

输出那行末尾是 disabled 说明禁用标记写进去了。但在 macOS 15.x 上,这不代表服务不会再跑——过一阵再 pgrep -f SoftwareUpdateNotificationManager,多半又能查到它,原因见文末补充。

整个过程只写了一个禁用标记,落点在:

/var/db/com.apple.xpc.launchd/disabled.<你的uid>.plist

没有动任何系统文件,也不需要关掉 SIP。

代价与恢复

关掉的只是 macOS 系统更新提醒这一条线:

  • 第三方软件自带的更新器照常提醒。查一下状态就能看到对比:
launchctl print-disabled gui/$(id -u) | grep -iE "update|appstore"
"com.microsoft.update.agent"  => enabled
"com.google.GoogleUpdater.wake" => enabled
"com.apple.SoftwareUpdateNotificationManager" => disabled
  • 真正的代价只有一项:系统安全更新也不弹提醒了。检测本身还在跑,想看的时候手动敲一条:
softwareupdate -l
  • 想恢复,把 disable 换成 enable,重启即可:
sudo launchctl enable gui/$(id -u)/com.apple.SoftwareUpdateNotificationManager

二、红点为什么清了又回来

弹窗没了,但是系统设置图标上那个红色 ① 还在。 搜到的常规方案是这条:

defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Dock

跑完纹丝不动。

原因不是命令写错了,而是它管的不是你这个场景:这条只负责「App 没在运行时」Dock 显示的角标。而你排查的时候一直开着系统设置——运行中的 App 会自己往图标上挂角标,正好每次都盖在你刚清完之后。所以观感就是「清了没反应」。

红标出现有三层原因 如果只是清理其中的一层, 红标还是会被其他层顶回来, 只有三层全部清完红色角标才会消失

层是什么顶回来的原因
DockAttentionPrefBundleIDs 键App 在运行就被进程盖写
进程运行中的系统设置自己挂标依据是下面那层缓存
缓存/Library/Preferences/com.apple.SoftwareUpdate 的 RecommendedUpdates每次成功检查更新就重新写入
# 挡住更新检查的域名(没挂代理时靠这两条)
# swscan 管常规更新目录,gdmf 管大版本升级信息,只挡一个照样会被发现新系统
sudo sh -c 'echo "0.0.0.0 swscan.apple.com" >> /etc/hosts'
sudo sh -c 'echo "0.0.0.0 gdmf.apple.com" >> /etc/hosts'
 
# 不再自动检查更新(这个键不持久,见下面的提示)
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -bool false
 
# 清掉"有更新可用"的历史缓存
sudo sh -c 'defaults delete /Library/Preferences/com.apple.SoftwareUpdate RecommendedUpdates; killall softwareupdated 2>/dev/null; true'
 
# 清掉图标上当前那个红点
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
osascript -e 'quit app "System Settings"'; killall Dock

这里有一个坑,如果使用了代理以上代码不会生效,执行完红点还在

以 Shadowrocket 为例,配置 [Rule] 段顶部加这两行:

DOMAIN,swscan.apple.com,REJECT
DOMAIN,gdmf.apple.com,REJECT

验收:重新打开系统设置,侧栏「有软件更新可用」整行消失,Dock 齿轮无红标。也可以直接查缓存是不是空的:

defaults read /Library/Preferences/com.apple.SoftwareUpdate | grep -A2 RecommendedUpdates

看到 RecommendedUpdates = ( ) 就对了。

代价与恢复

  • 屏蔽期间手动检查更新也会失败;走同一通道的 Xcode Command Line Tools 安装/更新、Rosetta 2 安装也会受影响。
  • 不影响 App Store 应用更新、iCloud、推送、日常浏览。
  • 想装更新时先删规则:删掉代理里那行,再执行
sudo sed -i '' -E '/(swscan|gdmf)\.apple\.com/d' /etc/hosts

环境说明

本文命令在 macOS 15.2 (24C101) 上实测。路径与键名在 macOS 13~15 上一致;更早的系统 launchctl print-disabled 输出格式可能略有差异,以实际输出为准。


补充

2026-09-25 更新。视频里演示的是上面「最简版」的命令;录完之后又弹了一次「有更新项目 / 新软件已可安装」,以下是对去处升级弹窗的补充

最简操补充

在前面「最简版」的基础上,再做两步。

第 1 步:终端执行(不要 sudo):

for k in UserNotificationDate MajorOSUserNotificationDate RecommendedUserNotificationDate; do
  defaults write com.apple.SoftwareUpdate "$k" -date "2099-01-01 00:00:00 +0000"
done

第 2 步:装描述文件。

  1. 复制下面闸二里那段 XML,然后在终端执行:
    cd ~/Desktop && pbpaste > mac-update-quiet.mobileconfig && plutil -lint mac-update-quiet.mobileconfig && open mac-update-quiet.mobileconfig
    看到 OK 说明文件没问题(别用「文本编辑」另存,原因见闸二);
  2. 打开 系统设置 → 通用 → 设备管理,双击「关闭 macOS 升级弹窗与红标」→ 安装 → 输入开机密码。

验收:下面这条能读出 DisableSoftwareUpdateNotifications = 1 就好了。

defaults read "/Library/Managed Preferences/com.apple.SoftwareUpdate"

想恢复:删掉描述文件,再执行文末「验收与恢复」里的三条 defaults delete。

下面是详细原理。

弹窗为何会复发

翻系统日志和 SoftwareUpdateNotificationManager 本体,查到了这几点:

  1. launchctl disable 挡不住它。 禁用列表里写着 disabled,但它是受 SIP 保护的系统服务,照样会被叫醒:

    launchctl print gui/$(id -u)/com.apple.SoftwareUpdateNotificationManager | grep -E "state|runs|immediate reason"
    state = running
    runs = 38
    immediate reason = xpc event
    
  2. 它弹窗用的是本地缓存,不用联网。 上面的 hosts / 代理规则确实把更新检查挡住了(日志里代理返回 404,扫描失败),可它看到「上次扫描失败」,就转头去读 /Library/Updates/ProductMetadata.plist——这里还存着几个月前那次成功扫描记下的 macOS Tahoe。这个目录带 SIP 的 restricted 标记,root 也删不掉。

  3. 弹不弹,要过两道判断。 日志原文:

    Check if exceeded last user notification update post date (...) with interval (259200.000000): 1
    Should show available updates notification? ... disableSUNotifications: 0, nowExceedsLastNotificationDate: 1 ...
    
    • nowExceedsLastNotificationDate:距上次弹窗是否已超过 3 天(259200 秒),所以它是隔几天来一次;
    • disableSUNotifications:一个总开关,只读描述文件(受管配置)。
  4. 命令行关不掉自动检查。 AutomaticCheckEnabled 写进去十几秒就被 softwareupdated 抹掉,softwareupdate --schedule off 因缺少私有权限被拒。

所以思路改成:缓存删不掉就不删,直接让这两道判断都不通过。

闸一:把「上次弹窗时间」推到 2099 年

普通用户执行,不要 sudo:

for k in UserNotificationDate MajorOSUserNotificationDate RecommendedUserNotificationDate; do
  defaults write com.apple.SoftwareUpdate "$k" -date "2099-01-01 00:00:00 +0000"
done

「距上次弹窗超过 3 天」从此一直不成立。日志里能看到它不弹的样子:nowExceedsLastNotificationDate: 0。

闸二:描述文件打开总开关

把下面这段复制下来,按下述命令在终端存成 mac-update-quiet.mobileconfig:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PayloadContent</key>
	<array>
		<dict>
			<key>PayloadType</key><string>com.apple.ManagedClient.preferences</string>
			<key>PayloadIdentifier</key><string>local.su-quiet.prefs</string>
			<key>PayloadUUID</key><string>6F1C2B7E-3A4D-4C51-9E0B-7D2A1F5C8E31</string>
			<key>PayloadVersion</key><integer>1</integer>
			<key>PayloadDisplayName</key><string>软件更新:静默</string>
			<key>PayloadContent</key>
			<dict>
				<key>com.apple.SoftwareUpdate</key>
				<dict>
					<key>Forced</key>
					<array>
						<dict>
							<key>mcx_preference_settings</key>
							<dict>
								<key>DisableSoftwareUpdateNotifications</key><true/>
								<key>DisableSoftwareUpdateBadging</key><true/>
								<key>AutomaticCheckEnabled</key><false/>
								<key>AutomaticDownload</key><false/>
							</dict>
						</dict>
					</array>
				</dict>
			</dict>
		</dict>
	</array>
	<key>PayloadDisplayName</key><string>关闭 macOS 升级弹窗与红标</string>
	<key>PayloadIdentifier</key><string>local.su-quiet</string>
	<key>PayloadUUID</key><string>B8E4D0A2-91C7-4F3E-A6B5-2C9D8E7F1A40</string>
	<key>PayloadType</key><string>Configuration</string>
	<key>PayloadScope</key><string>System</string>
	<key>PayloadVersion</key><integer>1</integer>
</dict>
</plist>

复制好之后在终端执行,直接从剪贴板生成文件、检查格式、打开:

cd ~/Desktop && pbpaste > mac-update-quiet.mobileconfig && plutil -lint mac-update-quiet.mobileconfig && open mac-update-quiet.mobileconfig

plutil -lint 输出 OK 才往下走。

别用「文本编辑」另存

「文本编辑」默认存成富文本(RTF),改个后缀名也还是 RTF。双击后设备管理里会多一条红色 Error,弹窗报 Could not open profile … (NSCocoaErrorDomain:3840),意思是文件解析不了。 遇到了就在设备管理里选中那条 Error,点「−」删掉,再用上面的 pbpaste 重新生成。实在要用「文本编辑」,存之前先点 格式 → 制作纯文本(⇧⌘T)。

再到 系统设置 → 通用 → 设备管理,双击它点「安装」(macOS 15 的描述文件在这里,不在「隐私与安全性」)。四项分别管:弹窗总开关、红标、自动检查、自动下载。这是走系统认可的受管配置通道,softwareupdated 抹不掉。

确认写进去了:

defaults read "/Library/Managed Preferences/com.apple.SoftwareUpdate"

验证结果(2026-09-25 起,2026-10-12 更新)

  • 红标开关已生效:日志从 Setting badge count to 0 (disabled: 0) 变成了 (disabled: 1)。
  • 时间戳这道闸的判定方式已从日志确认(nowExceedsLastNotificationDate: 0 时不弹)。
  • 总开关也已生效:10-07 到 10-12 日志里能查到的 6 次判定全是 disableSUNotifications: 1,而且当时 containsActiveUpdates: 1(确实有更新在等着装),说明系统读到了 DisableSoftwareUpdateNotifications 这个未公开的键。
  • 两道闸是互为保险的:时间戳哪天被系统重置了,还有描述文件兜底;反过来也一样。

验收与恢复

查它最近几次的判定:

/usr/bin/log show --last 14d --style syslog --predicate 'process == "SoftwareUpdateNotificationManager"' \
  | grep -E "Should show available|Post Update Available|Setting badge count" | tail -5

恢复:

defaults delete com.apple.SoftwareUpdate UserNotificationDate
defaults delete com.apple.SoftwareUpdate MajorOSUserNotificationDate
defaults delete com.apple.SoftwareUpdate RecommendedUserNotificationDate

再到 系统设置 → 通用 → 设备管理 删掉那个描述文件,并按上面「恢复提醒和红标」撤掉 hosts 和代理规则。