Добрый день! Есть 2 таблицы.
c:\nncron>cat A.txt
Lubos John Linda Rares Rick
A B C E
4 1 2 3
c:\nncron>cat B.txt
Anna Linda Rares John Max
F G E I
4 2 3 1
Таблицы не отсортированы. Нужно объединить их так, чтобы получить результирующую таблицу:
c:\nncron>cat C.txt
Lubos John Linda Rares Rick Anna Max
A B C E
4 1 2 3
E F G I
1 2 3 4
Насколько я понимаю, эта операция в терминологии баз данных называется FULL OUTER JOIN (полным объединением). Вот хотелось бы узнать, как его эффективно реализовать в bash стандартными утилитами unixutils - awk sed, sort, grep.
Anonymous уже
рекомендовал мне утилиту
join, но попользовавшись ей, я сделал вывод, что у нее есть 2 существенных недостатка.
Во первых, сравниваемые столбцы таблиц должны быть отсортированы по возрастанию плюс выполняется объединение строк, а не полей. То есть, для того, чтобы воспользоваться данной утилитой, эти таблицы нужно транспонировать, добавить недостающие TAB справа и отсортировать по сравниваемому полю, пусть это будет поле 1:
c:\nncron>gawk -F"\t" -v OFS="\t" "NF=5"
c:\nncron>gawk -F\x09 "{OFS = FS}{ for (i=1; i<=NF; i++) print i,NR,$i}" | sort -nk1,1 -k2,2 | gawk -F\x09 " NR>1 && $2==1 { print \"\" }; { printf \"%s\x09\",$3 }; END { print \"\" }" | cut -f 1-3 | sort -k1"
Получим:
c:\nncron>cat A_transp.txt
John A 1
Linda B 2
Lubos 4
Rares C 3
Rick E
c:\nncron>cat B_transp.txt
Anna 4
John E 1
Linda F 2
Max I
Rares G 3
В винде я столкнулся еще с одной промежуточной проблемой - невозможность использования TAB в качестве выходного разделителя (ключ \t), но это так, мелочи, опустим это.
Вторым существенным недостатком данной утилиты является то, что при использовании ключа -a2 непарные строки таблицы 2 в выводятся без отступов слева, то есть ячейки непарных строк таблицы 2 фактически находятся в полях таблицы 1, что усложняет дальнейшее построение объединенной таблицы.
Кроме того, если в исходной таблице идут 2 символа разделителя подряд, например 2 TAB, то в объединенной таблице будет один TAB. Опция -e, замещающая входные пустые ячейки, работает некорректно, заменяются не все пустые ячейки. Поэтому пустые ячейки исходной таблицы приходится заменять с помощью sed. Допустим, я делаю это и использую join:
c:\nncron>sed -i "s/\t\(\t\|$\)/\tNULL0\1/g" A_transp.txt
John A 1
Linda B 2
Lubos NULL0 4
Rares C 3
Rick E NULL0
c:\nncron>sed -i "s/\t\(\t\|$\)/\tNULL0\1/g" B_transp.txt
Anna NULL0 4
John E 1
Linda F 2
Max I NULL0
Rares G 3
Вот такая невыровненная хрень получается:
c:\nncron>join -j 1 -a1 -a2 A_transp.txt B_transp.txt
Anna NULL0 4
John A 1 E 1
Linda B 2 F 2
Lubos NULL0 4
Max I NULL0
Rares C 3 G 3
Rick E NULL0
Или если удалить пустые ячейки и заменить пробелы TAB:
c:\nncron>join -j 1 -a1 -a2 A_transp.txt B_transp.txt | sed -e "s/ /\t/g;s/NULL0//g"
Anna 4
John A 1 E 1
Linda B 2 F 2
Lubos 4
Max I
Rares C 3 G 3
Rick E
Сравните это с транспонированной матрицей C.txt (как должно быть в идеале):
c:\nncron>cat C.txt | gawk -F\x09 "{OFS = FS}{ for (i=1; i<=NF; i++) print i,NR,$i}" | sort -nk1,1 -k2,2 | gawk -F\x09 " NR>1 && $2==1 { print \"\" }; { printf \"%s\x09\",$3 }; END { print \"\" }" | cut -f 1-5
Lubos 4
John A 1 E 1
Linda B 2 F 2
Rares C 3 G 3
Rick E
Anna 4
Max I
Поскольку исходные таблицы были отсортированы, чтобы привести объединенную таблицу в начальный, не сортированный вид, строки транспонированных таблиц нужно было еще и пронумеровать до сортировки, чтобы потом их восстановить...
Вот собственно я хотел бы спросить форумчан, может быть кто из вас сталкивался с такой задачей и каким способом вы ее решали. Может быть, существует более легкий и просто способ. Желательно с помощью утилит unixutils, не прибегая к сторонним ЯП.