сравнить два столбца разных файлов и распечатать, если он совпадает

16

Я использую Solaris 10, поэтому параметры grep, включающие -f, не работают.

У меня есть два файла, разделенных каналом:

file1:

abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

файл 2:

abc|123|
kumar|pki|
cab|234

Я хотел бы сравнить первые два столбца файла file2 с файлом file1 (поиск по всему содержимому файла file1 в первых двух столбцах), если они совпадают, вывести соответствующую строку файла file1. Затем найдите вторую строку файла 2 и так далее.

Ожидаемый результат:

abc|123|BNY|apple|
cab|234|cyx|orange|

У меня огромные файлы, содержащие около 400 000 строк, поэтому я хотел бы ускорить их выполнение.

user68365
источник
Я удалил пробелы в ваших примерах, если хотите, пожалуйста, откатите редактирование назад. Помните, что пробелы значительны, вы должны иметь их, только если они существуют в ваших реальных файлах.
Тердон
Попробуйте использовать версию GNU grep, она под /usr/sfw/bin/ggrep. stackoverflow.com/questions/15259882/…
slm

Ответы:

21

Вот для чего был разработан awk:

$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|

объяснение

  • -F'|': устанавливает разделитель полей в |.
  • NR==FNR: NR - номер текущей строки ввода, а FNR - номер строки текущего файла. Два будут равны только во время чтения 1-го файла.
  • c[$1$2]++; next: если это первый файл, сохраните первые два поля в cмассиве. Затем перейдите к следующей строке, чтобы она применялась только к 1-му файлу.

  • c[$1$2]>0: блок else будет выполняться, только если это второй файл, поэтому мы проверяем, были ли уже видны поля 1 и 2 этого файла ( c[$1$2]>0), и если они были, мы печатаем строку. В awk, действие по умолчанию состоит в том, чтобы напечатать строку, поэтому, если значение c[$1$2]>0равно true, строка будет напечатана.


Кроме того, поскольку вы пометили с помощью Perl:

perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
         while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1

объяснение

Откроется первая строка file2, прочитайте все, вплоть до 2nd |( .+?\|[^|]+), и сохраните это ( $&результат последнего оператора соответствия) в %kхэше.

Вторая строка обрабатывает file1, использует одно и то же регулярное выражение для извлечения первых двух столбцов и печатает строку, если эти столбцы определены в %kхэше.


Оба вышеупомянутых подхода должны будут содержать 2 первых столбца файла 2 в памяти. Это не должно быть проблемой, если у вас есть только несколько сотен тысяч строк, но если это так, вы можете сделать что-то вроде

cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done

Но это будет медленнее.

Тердон
источник
Но не загрузит ли это все (первые два столбца) file2в память?
Джозеф Р.
@terdon: awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0'это более короткая версия.
cuonglm
это не работает ..
user68365
@ user68365: file2есть ли повторяющиеся строки?
cuonglm
НЕТ, у него нет повторяющихся строк
user68365
1

я думаю

grep -Ff file2 file1

это то, что вы ищете. Это должно быть эффективно, но я не уверен, что это будет так точно, как вы хотите. Если abc|123(например) найти в строке file1в разных столбцах, эта строка будет напечатана. Если вы можете гарантировать, что этого никогда не произойдет, вышеприведенная строка должна работать.

Джозеф Р.
источник
Grep будет недостаточно, поскольку где-то в этом файле может присутствовать abc | 123. Более того, я использую Solaris 10, и я тоже не могу использовать эту опцию grep.
user68365
2
@ user68365 уточни все это по твоему вопросу. Вы должны сообщить нам свою ОС и указать, что хотите соответствовать только первым двум столбцам.
Terdon
1

Если вы хотите думать о проблеме в SQL как-то иначе, то вам определенно стоит попробовать инструмент с именем ' q ':

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

Это более понятно и легко понять, если вы знакомы с запросами SQL.

Винсент
источник
Спасибо за одно из наименее загадочных решений, безусловно. Это то, что я хочу. Но у меня были некоторые проблемы с поиском этого «инструмента q»
Рольф
Очень полезный инструмент.
ghilesZ
0
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|
mr_tron
источник
1
Как я уже редактировал и упоминал в этом вопросе, параметры grep -f не работают в моей системе
user68365
Solaris 10 содержит основные утилиты GNU в / usr / sfw / bin Использовать / usr / sfw / bin / sed и / usr / sfw / bin / grep
mr_tron