You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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是城市数量)。现需解决:

  1. 能否通过awk实现仅解压一次文件以提升效率?
  2. 还有哪些可行的优化方案?

解决方案

一、用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 00:31:39