去重那一行又跳出来了:规则缺失,已补。
同一台机器,同一条规则,那天下午被它补了第四遍。
那阵子在给七亿批量远程管理工具做防火墙规则的增删和去重。逻辑不复杂:开规则前先看这条在不在,在就跳过,不在才建,判据只有一个——规则名。听上去挺稳,直到测试机上那条允许所有入站的规则被反复重装,我才反应过来,这判据不是偶发失灵,是稳定地不对。
先去查自己的代码。第一反应是名字里有空格或者大小写差异,两边比对时没归一化。于是加了 trim、转小写,全角半角也顺手过了一遍,重新跑。日志里对不上的那十二条,一条没少。
这里得补个背景。读规则我们走了两条路:老一些的代码用 netsh advfirewall firewall show rule name=all,后来新写的用 PowerShell 的 Get-NetFirewallRule。两边各自都能把规则列全,我就默认「名字」这东西在哪边都一样。
直到把同一条规则的两种输出并排打出来,我才看懂错在哪。netsh 给的是展开后的中文串,比如 "文件和打印机共享" 后面还跟着一长串;而 Get-NetFirewallRule.DisplayName 对一部分系统内置规则,返回的是一个资源引用名,长这样:@{Microsoft.Windows.xxx.ms-resource}。一个给你资源 key,一个给你展开后的文本,拿它们当同一个键,当然对不上。
内置规则的显示名本来就是本地化的。中文系统上 netsh 直接展开了,PowerShell 那条路在某些规则上没展开,原样把资源引用丢了回来。所以只要跨通道比——netsh 建、PS 读,或者反着来——就会把「有」判成「没有」,然后老老实实再建一遍。
我数了一遍:131 条规则里,两边名字对不上的是 12 条。这个比例很阴。手工点开抽查几条,正好都是对得上的,只有跑全量才露馅。当时我盯着那两串字符看了好一会儿,一度以为是自己终端的编码坏了,换了台机器打印,一模一样。
后面改了三处。第一,建规则和回读一条通道走到底,谁建的用谁读,不混着比。第二,去重的判据不再只认名字,补上「方向 + 协议 + 端口」这一组更稳的标识兜底。第三,加一条兜底回读:按名字没找到时,落到另一条通道再核一遍,别急着判缺失。

回头看,坑不难,难的是我一开始笃定「名字就是名字」。同一个字段,不同的 API 通道给出来的可能根本不是一回事,跨通道比对之前,先确认那个字段在两边的语义是不是同一个——这话听着像废话,可我在这上面实打实绕了半天。七亿批量远程管理工具管防火墙这一块,之前还栽过一回状态没管住的(把 3389 限了 IP,「允许所有人」那条却还亮着),路数是同一个:界面上看着对,不等于底下那条真按你说的在跑。
要拿它批量管 Windows 上的防火墙规则,七亿批量远程管理工具这块的用法在教程里写着了。
绕在「我以为两边一样」上的时间,比真正改这三处代码多,大概三倍不止。
评论