Использование bash для запроса большого файла с разделителями табуляции - PullRequest
0 голосов
/ 26 января 2020

У меня есть список имен и идентификаторов (50 записей)

cat input.txt

name    ID
Mike    2000
Mike    20003
Mike    20002

И есть огромный zip-файл (13 ГБ)

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

каждый $1"\t"$2 client.gz является уникальным идентификатором. Там могут быть некоторые записи из input.txt, которые могут отсутствовать в clients.gz. Таким образом, я хотел бы добавить столбец NR к моему выводу, чтобы выяснить, какие отсутствуют. Я хотел бы использовать zgrep. awk занимает очень много времени (так как я должен был zcat для распаковки сжатого файла, я предполагаю?)

Я знаю, что zgrep 'Mike\t2000' не работает. Я могу себе представить проблему NR с помощью awk FNR.

Пока у меня есть:

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

Ответы [ 3 ]

1 голос
/ 26 января 2020

[РЕДАКТИРОВАТЬ]
Я неправильно понял, откуда берутся номера строк. Исправлено.

Вы бы попробовали следующее:

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
  • Вторая строка и третья генерируют шаблон поиска как Mike 2000|Mike 20003|Mike 20002 из input.txt.
  • Строка for ((i=0; i<${#ary[@]}; i++)); do .. создает карту из шаблона в число.
  • Выражение "${num[$(cut -f 1-2 <<<"$line")]}" извлекает номер строки из 1-го и 2-го полей выходных данных .

Если производительность по-прежнему неудовлетворительная, рассмотрите ripgrep, что намного быстрее, чем grep или zgrep.

1 голос
/ 26 января 2020
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ key = $1 FS $2 }
NR == FNR { map[key] = (NR>1 ? NR-1 : "NR"); next }
key in map { print map[key], $0 }

$ zcat clients.gz | awk -f tst.awk input.txt -
NR      name    ID      comment
1       Mike    2000    foo
3       Mike    20002   bar
1 голос
/ 26 января 2020

С GNU awk и bash:

awk 'BEGIN{FS=OFS="\t"} 
     # process input.txt
     NR==FNR{
       a[$1,$2]=$1 FS $2
       line[$1,$2]=NR-1
       next
     }
     # process <(zcat clients.gz)
     {
       $4=a[$1,$2]
       if(FNR==1)
         line[$1,$2]="NR"
       if($4!="")
         print line[$1,$2],$1,$2,$3
     }' 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[$1,$2]=$1 FS $2; line[$1,$2]=NR-1; next} {$4=a[$1,$2]; if(FNR==1) line[$1,$2]="NR"; if($4!="")print line[$1,$2],$1,$2,$3}' input.txt <(zcat clients.gz)

См .: Объединение двух файлов на основе двух ключевых столбцов awk и 8 Встроенные переменные мощного Awk - FS, OFS, RS, ORS, NR, NF, FILENAME, FNR

...