Команда awk или sed для выбора столбцов и строк из нескольких файлов

Ищем команду для следующей задачи:

У меня есть три файла, каждый с двумя столбцами, как показано ниже. Я хотел бы создать file4 с четырьмя столбцами.

Вывод должен напоминать отсортированную версию file1, file2 и file3, так что первый столбец отсортирован, второй столбец — второй столбец file1, третий столбец — второй столбец file2, а четвертый столбец — второй столбец file3.

Записи в столбцах со 2 по 3 не должны быть отсортированы, но должны соответствовать паре "ключ-значение" в первом столбце исходных файлов.

Я пробовал пересечение в Linux, но не дал желаемых результатов.

Любая помощь будет оценена. Заранее спасибо!!

$ cat -- file1                
A1     B5
A10    B2
A3     B15
A15    B6
A2     B10
A6     B19
$ cat -- file2
A10 C4
A4  C8
A6  C5
A3  C10
A12 C14
A15 C18
$ cat -- file 3
A3  D1
A22 D9
A20 D3
A10 D5
A6  D10
A21 D11

$ cat -- file 4
col1 col2    col3    col4
A1   B5
A2   B10
A3   B15      C10     D1
A4            C8 
A6   B19      C5      D10
A10  B2       C4      D5
A12           C14
A15  B6       C18
A20                   D3
A21                   D11
A22                   D9

🤔 А знаете ли вы, что...
Bash позволяет работать с переменными окружения для настройки поведения скриптов.


1
91
4

Ответы:

Решено
Версия Awk + Bash:
( echo "col1, col2, col3, col4" &&
awk 'ARGIND==1 { a[$1]=$2; allkeys[$1]=1 } ARGIND==2 { b[$1]=$2; allkeys[$1]=1 } ARGIND==3 { c[$1]=$2; allkeys[$1]=1 }
    END{
        for (k in allkeys) {
            print k", "a[k]", "b[k]", "c[k]
        }
    }' file1 file2 file3 | sort -V -k1,1 ) | column -t -s ',' 
Чистая Bash-версия:
declare -A a
while read key value; do a[$key] = "${a[$key]:-}${a[$key]:+, }$value"; done < file1
while read key value; do a[$key] = "${a[$key]:-, }${a[$key]:+, }$value"; done < file2
while read key value; do a[$key] = "${a[$key]:-, , }${a[$key]:+, }$value"; done < file3

(echo "col1, col2, col3, col4" &&
for i in ${!a[@]}; do 
    echo $i, ${a[$i]}
done | sort -V -k1,1) | column -t -s ','

Пояснение к "${a[$key]:-, , }${a[$key]:+, }$value" пожалуйста, проверьте Shell-Parameter-Expansion


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

gawk '{ a[$1] = substr($1, 1); b[$1, ARGIND] = $2 }
    END {
        PROCINFO["sorted_in"] = "@val_num_asc"
        for (i in a) {
            t = i
            for (j = 1; j <= ARGIND; ++j)
                t = t OFS b[i, j]
            print t
        }
    }' file{1..3} | column -t

Существует простой инструмент под названием join, который позволяет выполнять эту операцию:

#!/usr/bin/env bash
cut -d ' ' -f1 file{1,2,3} | sort -k1,1 -u > ftmp
for f in file1 file2 file3; do
   mv -- ftmp file4
   join -a1 -e "---" -o auto file4 <(sort -k1,1 "$f") > ftmp
done
sort -k1,1V ftmp > file4
cat file4

Это выводит

A1 B5 --- ---
A2 B10 --- ---
A3 B15 C10 D1
A4 --- C8 ---
A6 B19 C5 D10
A10 B2 C4 D5
A12 --- C14 ---
A15 B6 C18 ---
A20 --- --- D3
A21 --- --- D11
A22 --- --- D9

Я использовал --- для обозначения пустого поля. Если вы хотите красиво напечатать это, вам нужно повторно проанализировать его с помощью awk или чего-либо еще.


Это может сработать для вас (GNU sed и sort):

s=''; for f in file{1,2,3}; do s = "$s\t"; sed -E "s/\s+/$s/" $f; done |
sort -V | 
sed -Ee '1i\col1\tcol2\tcol3\tcol4' -e ':a;N;s/^((\S+\t).*\S).*\n\2\t+/\1\t/;ta;P;D'

Замените пробелы на табуляции и вставьте количество табуляции между ключом и значением в зависимости от того, какой файл обрабатывается.

Отсортируйте вывод по ключевому порядку столбцов.

Объедините каждую строку с ее ключом и распечатайте результат.