порівняйте два стовпчики різних файлів та друкуйте, якщо вони відповідають


16

Я використовую Solaris 10 і так виправляти варіанти із залученням -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 рядків, тому я хотів би зробити виконання швидким.


Я видалив провідні пробіли з ваших прикладів, якщо ви цього хочете, будь ласка, відкатіть редагування назад. Пам’ятайте, що пробіли є вагомими, їх слід мати лише у тому випадку, якщо вони існують у ваших фактичних файлах.
тердон

Спробуйте скористатися версією 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 - номер рядка поточного файлу. Два будуть рівні лише під час читання першого файлу.
  • c[$1$2]++; next: якщо це перший файл, збережіть перші 1 поля в cмасиві. Потім перейдіть до наступного рядка, щоб це застосовано лише до 1-го файлу.

  • c[$1$2]>0: блок else буде виконаний лише у випадку, якщо це другий файл, тому ми перевіримо, чи вже були помічені поля 1 та 2 цього файлу ( c[$1$2]>0), і чи були вони, друкуємо рядок. У awk, дією за замовчуванням є друк рядка, тому, якщо c[$1$2]>0це правда, рядок буде надруковано.


Крім того, оскільки ви позначили Perl:

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

Пояснення

Перший рядок відкриється file2, прочитайте все до 2-го |( .+?\|[^|]+) і збережіть це ( $&результат результату оператора останньої відповідності) у %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, будь ласка, уточніть це у своєму запитанні. Вам потрібно повідомити нам вашу ОС та вказати, що ви хочете відповідати лише першим 2 стовпцям.
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|

1
Як я редагував і згадував у запитанні, опції grep -f не працюють у моїй системі
user68365

Solaris 10 має gnu core-utils в / usr / sfw / bin Use / usr / sfw / bin / sed та / usr / sfw / bin / grep
mr_tron
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.