使用 bash 查询大型制表符分隔文件

Using bash to query a large tab delimited file

我有一个姓名和 ID 列表(50 个条目)

cat input.txt

name    ID
Mike    2000
Mike    20003
Mike    20002

还有一个巨大的压缩文件 (13GB)

zcat clients.gz

name    ID  comment
Mike    2000    foo
Mike    20002   bar
Josh    2000    cake
Josh    20002   _

我的预期输出是

NR  name    ID  comment
1    Mike   2000    foo
3    Mike   20002   bar

clients.gz 中的每个 "\t" 都是唯一标识符。 input.txt 中可能有一些条目可能在 clients.gz 中缺失。因此,我想将 NR 列添加到我的输出中以找出缺少的内容。我想使用 zgrep。 awk 需要很长时间(因为我必须 zcat 来解压缩我假设的压缩文件?)

我知道 zgrep 'Mike\t2000' 行不通。我想我可以用 awk FNR 解决 NR 问题。

到目前为止我有:

awk -v q="'" 
'
NR > 1 {
print "zcat clients.gz | zgrep -w $" q[=14=]q
}' input.txt |
bash > subset.txt

使用 GNU awk 和 bash:

awk 'BEGIN{FS=OFS="\t"} 
     # process input.txt
     NR==FNR{
       a[,]= FS 
       line[,]=NR-1
       next
     }
     # process <(zcat clients.gz)
     {
       =a[,]
       if(FNR==1)
         line[,]="NR"
       if(!="")
         print line[,],,,
     }' input.txt <(zcat clients.gz)

输出:

NR      name    ID      comment
1       Mike    2000    foo
3       Mike    20002   bar

作为一行:

awk 'BEGIN{FS=OFS="\t"} NR==FNR{a[,]= FS ; line[,]=NR-1; next} {=a[,]; if(FNR==1) line[,]="NR"; if(!="")print line[,],,,}' input.txt <(zcat clients.gz)

参见:Joining two files based on two key columns awk and 8 Powerful Awk Built-in Variables – FS, OFS, RS, ORS, NR, NF, FILENAME, FNR

[编辑]
我误解了前置行号的来源。更正。

你会尝试以下方法吗:

declare -A num          # asscoiates each pattern to the line number
mapfile -t ary < <(tail -n +2 input.txt)
pat=$(IFS='|'; echo "${ary[*]}")
for ((i=0; i<${#ary[@]}; i++)); do num[${ary[i]}]=$((i+1)); done
printf "%s\t%s\t%s\t%s\n" "NR" "name" "ID" "comment"
zgrep -E -w "$pat" clients.gz | while IFS= read -r line; do
    printf "%d\t%s\n" "${num[$(cut -f 1-2 <<<"$line")]}" "$line"
done

输出:

NR  name    ID  comment
1   Mike    2000    foo
3   Mike    20002   bar
  • 第二行和第三行从 input.txt.
  • 生成搜索模式 Mike 2000|Mike 20003|Mike 20002
  • for ((i=0; i<${#ary[@]}; i++)); do .. 从 图案到数字。
  • 表达式"${num[$(cut -f 1-2 <<<"$line")]}"检索行 来自输出的第一个和第二个字段的数字。

如果性能还不够理想,请考虑ripgrep,比grepzgrep快很多。

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ key =  FS  }
NR == FNR { map[key] = (NR>1 ? NR-1 : "NR"); next }
key in map { print map[key], [=10=] }

$ zcat clients.gz | awk -f tst.awk input.txt -
NR      name    ID      comment
1       Mike    2000    foo
3       Mike    20002   bar