能否在grep中同时实现过滤与统计?大文件处理优化咨询
优化百万行EPF文件的过滤+统计流程
你的思路方向是对的,但当前的方案确实存在重复扫描文件的问题,而且perl命令里的单引号嵌套还会导致语法错误。我来给你提供一个更高效的方案,只需要扫描原EPF文件一次就能完成过滤和统计更新,节省大量时间。
先说说你当前命令的问题
- 重复扫描开销:你先用
grep生成临时文件,再用grep -c统计临时文件行数,等于对过滤后的文件又做了一次全量扫描,虽然临时文件比原文件小,但百万级别的数据依然会增加耗时。 - 语法错误:你的perl命令里单引号嵌套了,
's/#recordsWritten:\d{7,8}/#recordsWritten:grep -e '^\d' -c appprice_temp/'这里面的'^\d'会被shell当成命令结束符,导致命令执行失败。
最优方案:用awk一次完成所有操作
awk可以在一次扫描原文件的过程中,同时完成读取过滤词、过滤匹配行、统计行数、更新recordsWritten这几个步骤,完全避免额外的文件IO操作。
场景1:只输出匹配行,最后追加统计行
如果你的需求是过滤出所有匹配的行,然后在输出文件的最后一行加上#recordsWritten:X,可以用这个命令:
awk -v filter_file="filtrowanie" ' BEGIN { # 读取过滤词文件,拼接成正则表达式 while ((getline line < filter_file) > 0) { regex = regex (regex == "" ? "" : "|") line } close(filter_file) count = 0 } # 匹配过滤词的行,打印并计数 $0 ~ regex { print count++ } # 最后输出统计行 END { print "#recordsWritten:" count }' application_price_old > appprice_final
场景2:保留原文件元数据,仅更新recordsWritten
如果原EPF文件里本身就有#recordsWritten:这类元数据行,你需要保留其他元数据,只更新这个统计数字,同时过滤数据行,用这个脚本:
awk -v filter_file="filtrowanie" ' BEGIN { # 把过滤词存入数组,方便匹配 while ((getline line < filter_file) > 0) { filters[line] = 1 } close(filter_file) count = 0 record_line = "" } # 处理元数据行(以#开头的行) /^#/ { if ($0 ~ /^#recordsWritten:/) { # 记录原统计行,后面替换数字 record_line = $0 next } # 其他元数据行直接打印 print next } # 数据行匹配过滤词则打印并计数 $0 ~ filters { print count++ } # 最后输出更新后的统计行 END { sub(/:[0-9]+$/, ":" count, record_line) print record_line }' application_price_old > appprice_final
为什么这个方案更好?
- 单次扫描:只需要读取一次原EPF文件,避免了两次大文件IO的耗时,对于百万行的文件来说,效率提升非常明显。
- 无临时文件:直接输出到最终文件,减少磁盘读写操作。
- 灵活可控:可以根据EPF文件的实际格式调整逻辑,比如保留元数据、处理特殊行等。
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

