如何高效实现仅删除黑名单中后缀匹配白名单的域名条目?
需求与高效解决方案
需求
删除广告黑名单文件中的行,仅当该行末尾精确匹配白名单文件中的条目时执行删除;若白名单条目出现在行开头或中间,该行需保留。
示例:
- 黑名单:
randomsites.com、calendar.google.com、google.com、google.com.fake.com - 白名单:
google.com - 处理后保留:
randomsites.com、google.com.fake.com
现有命令对比
- 功能达标但速度慢(OpenWrt下处理30万行需数分钟):
awk 'FNR==NR{a[$0];next} {for (i in a) {if ($0 ~ i "$") next}}1' /tmp/whitelist /tmp/blocklist > /tmp/new_blocklist
问题:每行遍历所有白名单条目,且使用正则匹配,CPU开销极大。
- 速度极快但仅支持整行匹配(处理仅需几秒):
awk 'NR==FNR{a[$0];next} !($0 in a)' /tmp/whitelist /tmp/blocklist > /tmp/tempfile
问题:无法满足后缀匹配需求。
优化后的高效后缀匹配命令
以下命令兼顾速度与需求,性能接近整行匹配的快速命令:
NR==FNR { # 收集白名单后缀及对应长度 suffixes[++cnt] = $0 suffix_lens[cnt] = length($0) next } # 首次处理黑名单时,按后缀长度降序排序(长后缀优先匹配,减少比较次数) FNR==1 { for (i=1; i<cnt; i++) { for (j=i+1; j<=cnt; j++) { if (suffix_lens[i] < suffix_lens[j]) { # 交换后缀 t = suffixes[i]; suffixes[i] = suffixes[j]; suffixes[j] = t # 交换长度 t = suffix_lens[i]; suffix_lens[i] = suffix_lens[j]; suffix_lens[j] = t } } } } { line_len = length($0) for (i=1; i<=cnt; i++) { s_len = suffix_lens[i] # 行长度不足时直接跳过,否则截取末尾字符串对比 if (line_len >= s_len && substr($0, line_len - s_len + 1) == suffixes[i]) { next # 匹配到后缀,跳过输出 } } print # 未匹配任何后缀,保留该行 }
性能提升原因
- 用字符串截取对比替代正则匹配,大幅降低CPU开销;
- 对白名单后缀按长度降序排序,优先匹配长后缀,减少不必要的比较次数;
- 避免了原慢命令中每行遍历整个白名单数组的低效逻辑。
使用方式(单行版):
awk 'NR==FNR{suffixes[++cnt]=$0;suffix_lens[cnt]=length($0);next}FNR==1{for(i=1;i<cnt;i++){for(j=i+1;j<=cnt;j++){if(suffix_lens[i]<suffix_lens[j]){t=suffixes[i];suffixes[i]=suffixes[j];suffixes[j]=t;t=suffix_lens[i];suffix_lens[i]=suffix_lens[j];suffix_lens[j]=t}}}}{line_len=length($0);for(i=1;i<=cnt;i++){s_len=suffix_lens[i];if(line_len>=s_len&&substr($0,line_len-s_len+1)==suffixes[i]){next}}print}' /tmp/whitelist /tmp/blocklist > /tmp/new_blocklist
内容的提问来源于stack exchange,提问作者Wizballs
相关产品推荐
相关产品推荐

