如何用awk优化zgrep?实现仅解压一次文件提升批量查询效率
问题描述
需要从输入目录/c/Users/Roy/DataReceived下的压缩文件中提取指定城市信息,存入输出目录/c/Users/Roy/Documents/Result下对应城市名的TXT文件(如NewYork.txt)。具体场景为:基于list_of_cities.txt中的城市名,在20个城市相关的.vcf.gz文件中匹配信息。
现有可运行的bash脚本如下:
#!/bin/bash declare INPUT_DIRECTORY=/c/Users/Roy/DataReceived declare OUTPUT_DIRECTORY=/c/Users/Roy/Documents/Result while read -r city; do echo $city zgrep -Hwi "$city" "${INPUT_DIRECTORY}/"*.vcf.gz > "${OUTPUT_DIRECTORY}/${city}.txt" done < list_of_cities.txt
但该脚本运行耗时约一周,核心问题是每个城市都要重复解压所有20个.gz文件,相当于做了N次重复解压(N是城市数量)。现需解决:
- 能否通过awk实现仅解压一次文件以提升效率?
- 还有哪些可行的优化方案?
解决方案
一、用awk实现仅解压一次文件
这个方案能彻底避免重复解压,一次性处理所有.gz文件,批量匹配所有城市:
#!/bin/bash INPUT_DIR="/c/Users/Roy/DataReceived" OUTPUT_DIR="/c/Users/Roy/Documents/Result" CITY_LIST="list_of_cities.txt" # 先创建输出目录(防止不存在报错) mkdir -p "$OUTPUT_DIR" # 一次性解压所有.gz文件,交给awk处理 zcat "$INPUT_DIR"/*.vcf.gz | awk -v out_dir="$OUTPUT_DIR" ' BEGIN { # 把城市列表加载到数组里,忽略空行 while ((getline city < "'"$CITY_LIST"'") > 0) { if (city != "") { cities[tolower(city)] = city } } close("'"$CITY_LIST"'") } { # 把当前行转小写,遍历城市数组做匹配 line_lower = tolower($0) for (city_lower in cities) { if (index(line_lower, city_lower) != 0) { # 模拟zgrep的-H参数,保留原文件名前缀,写入对应城市的文件 print FILENAME ":" $0 >> out_dir "/" cities[city_lower] ".txt" # 如果一行只需要匹配第一个城市就停止,可加break;要匹配所有就保留循环 } } } '
核心优势:
- 仅用
zcat解压所有.gz文件一次,彻底消除重复解压的耗时 - awk提前加载所有城市到数组,逐行处理时直接匹配,无需反复读取城市列表
- 用
index()做子串匹配,比正则匹配更高效;tolower()实现不区分大小写的匹配(对应原脚本的-i参数)
二、其他可行优化方案
1. 预处理压缩文件(适合重复需求)
如果后续还要做类似提取,可以先把所有.vcf.gz解压合并成一个大文件,后续直接读取这个文件:
zcat "$INPUT_DIR"/*.vcf.gz > "$INPUT_DIR/all_cities.vcf"
之后处理时直接读all_cities.vcf,完全省去解压步骤,磁盘空间足够的话这是最直接的优化。
2. 多核并行处理
利用CPU多核并行处理不同的.gz文件,比如用parallel工具(需先安装,如brew install parallel或apt install parallel):
parallel 'zcat {} | awk -v out_dir="$OUTPUT_DIR" -v city_list="$CITY_LIST" '\'' BEGIN { while ((getline city < city_list) > 0) { if (city != "") cities[tolower(city)] = city } close(city_list) } { line_lower = tolower($0) for (c in cities) { if (index(line_lower, c) != 0) print FILENAME ":" $0 >> out_dir "/" cities[c] ".txt" } }'\'' ::: "$INPUT_DIR"/*.vcf.gz
每个.gz文件单独分配一个进程处理,能大幅缩短总耗时。
3. 优化匹配逻辑
- 把原脚本的
zgrep -Hwi改成zgrep -HFi,用-F开启固定字符串匹配,比正则匹配更快(如果城市名没有特殊字符的话) - 先给城市列表去重:
sort -u list_of_cities.txt > unique_cities.txt,减少不必要的匹配次数
4. 磁盘IO优化
- 把输入目录和输出目录放在不同的物理磁盘上,避免读写操作互相干扰
- 如果用的是机械硬盘,尽量减少随机写操作(上述脚本都是按行追加,已经比较友好)
内容的提问来源于stack exchange,提问作者RoyBatty
相关产品推荐
相关产品推荐

