Использование AWK для объединения двух файлов на основе нескольких условий - PullRequest
0 голосов
/ 21 января 2019

Я знаю, что этот вопрос задавался несколько раз раньше. Вот один пример:

Использование AWK для объединения двух файлов на основе нескольких столбцов

Моя цель - распечатать столбцы 2, 4, 5 и 7 файла file_b и столбцы 17 и 18 файла file_a, если будет найдено следующее совпадение: Столбцы 2, 6 и 7 файла file_a.csv соответствуют столбцам 2, 4 и 5 файла file_b.csv соответственно.

Но как бы я ни старался, я не могу заставить его работать в моем случае. Вот мои два файла:

file_a.csv

col2, col6, col7, col17, col18
a, b, c, 145, 88
e, f, g, 101, 96
x, y, z, 243, 222

file_b.csv

col2, col4, col5, col7
a, b, c, 4.5
e, f, g, 6.3
x, k, l, 12.9

Вывод должен выглядеть так:

col2, col4, col5, col7, col17, col18
a, b, c, 4.5, 145, 88
e, f, g, 6.3, 101, 96

Я пробовал это:

awk -F, -v RS='\r\n' 'NR==FNR{key[$2 FS $6 FS $7]=$17 FS $18;next} {if($2 FS $4 FS $5 in key); print $2 FS $4 FS $5 FS $7 FS key[$2 FS $6 FS $7]}' file_a.csv file_b.csv > out.csv

В настоящее время я получаю вывод:

col2, col4, col5, col7,
a, b, c, 4.5,
e, f, g, 6.3,

Другими словами, col17 и col18 из file_a не отображаются.

Вчера я задал связанный вопрос, где у меня были проблемы с переносами строк. На этот вопрос ответили и решили, но теперь я думаю, что эта проблема связана с проверкой условия if.

Обновление: Я делюсь ссылками на усеченные копии фактических данных. Единственная разница между этими файлами и реальными состоит в том, что у настоящих есть миллионы строк. У этих есть только 10 каждый.

file_a.csv

file_b.csv

Ответы [ 3 ]

0 голосов
/ 21 января 2019
$ awk 'BEGIN      {RS="\r\n"; FS=OFS=","}
       NR==FNR    {a[$2,$6,$7]=$17 OFS $18; next} 
  ($2,$4,$5) in a {print $2,$4,$5,$7,a[$2,$4,$5]}' file1 file2 > output

Ваша главная проблема в том, что при поиске массива индекс, который вы должны использовать, - это второй ключ файла, а не первый ключ файла. Также точка с запятой после условия if неверна. Остальное только косметика.

Не уверен, что вы хотите, чтобы вывод \r\n был прерван, если также установлен ORS=RS, в противном случае это только перевод строки.

0 голосов
/ 21 января 2019

Поскольку вы упомянули, что файл огромен, вы можете попробовать Perl, если это вариант.

Предполагается, что файлы имеют "\ r".

$ cat file_a.csv
col2, col6, col7, col17, col18
a, b, c, 145, 88
e, f, g, 101, 96
x, y, z, 243, 222
$ cat file_b.csv
col2, col4, col5, col7
a, b, c, 4.5
e, f, g, 6.3
x, k, l, 12.9
$ perl -F, -lane 'BEGIN { %kv=map{chomp;chop;@a=split(",");"$a[0],$a[1],$a[2]"=>"$a[3]"} qx(cat file_b.csv) } if($.>1){ $x="$F[0],$F[1],$F[2]";chomp($F[-1]);print "$x,$kv{$x}",join(",",@F[-2,-1]) if $kv{$x} } ' file_a.csv
a, b, c, 4.5 145, 88
e, f, g, 6.3 101, 96
$
0 голосов
/ 21 января 2019

Пожалуйста, попробуйте это (GNU sed):

awk 'BEGIN{RS="\r\n";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}'

Это время, когда включается блок BEGIN. Также * включается OFS.
Когда мы печатаем много полей, которые отделяютсяТочно так же мы можем установить OFS и просто поставить запятую между вещами, которые мы хотим напечатать.

Нет необходимости проверять key in arr, когда вы присвоили значение для ключа в массиве,
по умолчанию, когда arr[somekey] не назначен ранее, это empty / "" и оценивается в false в awk (0 в скалярном контексте), а непустая строка оценивается в true (буквально true и false в awk) нет.
(Вы использовали неправильное array имя, $2,$6,$7 - это ключ в массиве arr здесь. Использование key в качестве имени массива сбивает с толку.)

Вы можете протестировать простую концепциюнапример:

awk 'BEGIN{print arr["newkey"]}'

Вам не нужен входной файл для выполнения блока BEGIN.

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

Обновление : Ваши файлы на самом деле заканчиваются на \n, если вы не можете быть уверены в окончании строки, используйте это:

awk 'BEGIN{RS="\r\n|\n|\r";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}' file_a.csv file_b.csv

или this (Thisодин будет игнорировать пустые строки):

awk 'BEGIN{RS="[\r\n]+";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}' file_a.csv file_b.csv

Также , лучше сначала конвертировать, чтобы избежать таких ситуаций:

sed -i 's/\r//' files

Или вы можете использовать dos2unix command:

dos2unix file

Это удобный инструмент командной строки, выполняющий только вышеуказанные действия.
Вы можете установить его, если его еще нет в вашей системе.
После преобразования вы не сможетев обычных ситуациях нужно присваивать RS.

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