Объединение множества файлов на основе соответствующего столбца - PullRequest
3 голосов
/ 30 апреля 2019

У меня много файлов (я выложил 5 в качестве примера)

Если нет совпадения с 1-м файлом, тогда 0 должно быть добавлено в вывод

file1

1001 1 2
1002 1 2
1003 3 5
1004 6 7
1005 8 9
1009 2 3

file2

1002 7
1003 8

file3

1001 5
1002 3

file4

1002 10
1004 60
1007  4

file5

1001 102
1003 305
1005 809

желаемый вывод

1001 1 2 0 5  0 102
1002 1 2 7 3 10   0
1003 3 5 8 0  0 305
1004 6 7 0 0 60   0
1005 8 9 0 0  0 809
1007 0 0 0 0  4   0
1009 2 3 0 0  0   0

Используя приведенный ниже код, я могу объединить два файла,НО как слить все

awk 'FNR==NR{a[$1]=$2;next}{print $0,a[$1]?a[$1]:"0"}' file2 file1

1001 1 2 0
1002 1 2 7
1003 3 5 8
1004 6 7 0
1005 8 9 0

Заранее спасибо

Ответы [ 3 ]

5 голосов
/ 30 апреля 2019

GNU Присоединяйтесь к спасателю!

$ join -a1 -a2 -e '0' -o auto file1 file2 \
  | join -a1 -a2 -e '0' -o auto - file3   \
  | join -a1 -a2 -e '0' -o auto - file4   \
  | join -a1 -a2 -e '0' -o auto - file5

Опции -a1 и -a2 говорят join вставить пропущенные поля.и -e '0' говорит ему заменить их на ноль.Выходные данные указываются с помощью -o auto, который предполагает заполнение всех полей.

При наличии большого количества файлов вы не можете использовать конструкцию конвейера, но вы можете использовать простой цикл for:

out=output
tmp=$(mktemp)
[[ -e "$out" ]] && rm -rf "$out" || touch "$out"
for file in f*; do
    join -a1 -a2 -e0 -o auto "$out" "$file" > "$tmp"
    mv "$tmp" "$out"
done
cat "$out"

или если вам действительно нравится конвейер:

pipeline="cat /dev/null"
for file in f*; do pipeline="$pipeline | join -a1 -a2 -e0 -o auto - $file"; done
eval "$pipeline"

очень интересный здесь: Есть ли ограничение на количество труб, которые я могу использовать?


Примечание: использование auto чрезвычайно полезно в этом случае, но не является частью стандарта POSIX .Это расширение GNU, которое является частью GNU coreutils .Чистая версия POSIX будет выглядеть немного более громоздкой, как:

$ join -a1 -a2 -e '0' -o 0 1.2 2.2 file1 file2 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 2.2 - file3 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 2.2 - file4 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 1.5 2.2 - file5

Дополнительная информация о man join

3 голосов
/ 30 апреля 2019

С GNU awk для истинных многомерных массивов и sorted_in:

$ cat tst.awk
FNR==1 { numCols = colNr }
{
    key = $1
    for (i=2; i<=NF; i++) {
        colNr = numCols + i - 1
        val   = $i
        lgth  = length(val)
        vals[key][colNr] = val
        wids[colNr] = (lgth > wids[colNr] ? lgth : wids[colNr])
    }
}
END {
    numCols = colNr
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (key in vals) {
        printf "%s", key
        for (colNr=1; colNr<=numCols; colNr++) {
            printf "%s%*d", OFS, wids[colNr], vals[key][colNr]
        }
        print ""
    }
}

$ awk -f tst.awk file*
1001 1 2 0 5  0 102
1002 1 2 7 3 10   0
1003 3 5 8 0  0 305
1004 6 7 0 0 60   0
1005 8 9 0 0  0 809
1007 0 0 0 0  4   0
1009 2 3 0 0  0   0
2 голосов
/ 30 апреля 2019

Использование GNU awk

awk '
NR>FNR && FNR==1{
colcount+=cols
}
{
for(i=2;i<=NF;i++){
  rec[$1][colcount+i-1]=$i
}
} 
{
cols=NF-1
}
END{
  colcount++
  for(ind in rec){
    printf "%s%s",ind,OFS
    for(i=1;i<=colcount;i++){
      printf "%s%s",rec[ind][i]?rec[ind][i]:0,OFS
    }
    print ""
  }
}' file{1..5} | sort -k1 | column -t

выход

1001  1  2  0  5  0   102
1002  1  2  7  3  10  0
1003  3  5  8  0  0   305
1004  6  7  0  0  60  0
1005  8  9  0  0  0   809
1006  0  0  0  0  0   666

Примечание: Будет работать для упомянутого случая здесь и для любого типа значений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...