Знайти вміст одного файлу з іншого файлу в UNIX

У мене є 2 файли. Перший файл містить список ідентифікаторів рядків кортежів таблиці в базі даних. А другий файл містить SQL-запити з цими ідентифікаторами рядків у пункті "where" запиту.

Наприклад:

Файл 1

1610657303
1610658464
1610659169
1610668135
1610668350
1610670407
1610671066

Файл 2

update TABLE_X set ATTRIBUTE_A=87 where ri=1610668350;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672154;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610668135;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672153;

I have to read Файл 1 and search in Файл 2 for all the SQL commands which matches the row ID's from Файл 1 and dump those SQL queries in a third file.

Файл 1 has 1,00,000 entries and Файл 2 contains 10 times the entries of Файл 1 i.e. 1,00,0000.

I used grep -f File_1 File_2 > File_3. But this is extremely slow and the rate is 1000 entries per hour.

Чи є більш швидкий спосіб зробити це?

22
Чи всі запити, які використовують ідентифікатори рядків, вказані у файлі 1, потрапляють у той же вихідний файл, або ви хочете мати окремий файл для кожного ID рядка у файлі 1?
додано Автор Frerich Raabe, джерело
Велике спасибі за grep -f file1 файл 2 . Це працює для мене. У мене 3 мільйони рядків, і це працює.
додано Автор Yogesh Darji, джерело
Чи дозволяється сортувати файли на ідентифікаторах? Або має бути збережений порядок?
додано Автор fizzer, джерело
Просто спробував підхід grep -f . Довелося вбити grep, перш ніж вона з'їла всю мою оперативну пам'ять :) А як щодо імпорту даних в пару таблиць SQL і дозволити sqlite або MySQL обробляти пошук?
додано Автор Marcello Romani, джерело
Ви, мабуть, маєте на увазі 1000000 і 10,000,000 права? :-)
додано Автор Marcello Romani, джерело
Порядок @fizzer не має значення. Ми можемо його сортувати.
додано Автор Tirthankar, джерело
Тільки один вихідний файл. Кожен раз, коли запис з файлу 1 збігається з записом у файлі 2, запис у файлі 2 повинен бути скинутий у вихідний файл. А записи у файлі 1 і файлі 2 є унікальними. У жодному з файлів немає повторюваних рядків.
додано Автор Tirthankar, джерело

8 Відповіді

You don't need regexps, so grep -F -f file1 file2

28
додано
але без атрибута -F
додано Автор Damian0o, джерело
На моїй машині: grep => 38.4s реальний; awk => 53.8s реальний; file1 19 ^ 6 рядків, file2 10 ^ 7 рядків.
додано Автор Marcello Romani, джерело
ВП каже, що він вже спробував цей, але це було занадто повільно.
додано Автор Marcello Romani, джерело
Я зацікавлений, як це виходить проти JS 's awk рішення , але я занадто ледачий, щоб повторити стан оператора.
додано Автор doubleDown, джерело
Я виконав те ж випробування на своїй машині, і результати дуже хороші з grep -F, важливо відзначити, що -F (captial) Інтерпретувати шаблон як список фіксованих рядків (замість регулярних виразів), розділених новими рядками, з яких має бути узгоджено, тому є надзвичайно швидким порівняно з awk або регулярним grep з просто -f. Сподіваюся, що це допомагає!
додано Автор PetPan, джерело

В одну сторону з awk :

awk -v FS="[ =]" 'NR==FNR{rows[$1]++;next}(substr($NF,1,length($NF)-1) in rows)' File1 File2

Це має бути досить швидким. На моїй машині було потрібно 2 секунди для створення 1 мільйона записів і порівняння його з 3 мільйонами рядків.

Характеристики машини:

Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz (8 cores)
98 GB RAM
13
додано
Що робити, якщо для ключа у файлі 1 є декілька різних записів у файлі 2?
додано Автор dushyantashu, джерело
@DoubleDown Це не моя особиста машина :) Це мій тест на робочому місці. Правда, ми можемо зробити це теж .. просто не хотілося зупиняти надто багато роздільників поля.
додано Автор jaypal singh, джерело
@doubleDown Хороша точка. Додано специфікації.
додано Автор jaypal singh, джерело
+1. awk для перемоги.
додано Автор undur_gongor, джерело
Цей один лайнер блискучий :)
додано Автор Marcello Romani, джерело
Якщо ви збираєтеся вказати час, необхідний для запуску команди, може бути доречним включити специфікації машини.
додано Автор doubleDown, джерело
Ваша машина - звір. До речі, якщо ви додаєте ; до FS, тобто FS = "[=;]" , можна просто скористатися $ (NF-1) , щоб отримати числа w/o substr та co. Не впевнений, як це буде вплив продуктивності, хоча.
додано Автор doubleDown, джерело
@JS is Це одна дуже приваблива. Загальний час, витрачений на мою машину, становив 46 хвилин. Характеристики машини: SunOS 5.10 Generic_127111-03 sun4v sparc SUNW, SPARC-Enterprise-T5120 Дякуємо!
додано Автор Tirthankar, джерело

Можливо, я щось відсутній, але чи не достатньо просто повторити ідентифікатори в file1 і для кожного ID, grep file2 і зберегти збіги в третій файл? Тобто.

 for ID in `cat file1`; do grep $ID file2; done > file3

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

Нижче наведено версію Python цієї ідеї:

queryByID = {}

for line in file('file2'):
  lastEquals = line.rfind('=')
  semicolon = line.find(';', lastEquals)
  id = line[lastEquals + 1:semicolon]
  queryByID[id] = line.rstrip()

for line in file('file1'):
  id = line.rstrip()
  if id in queryByID:
    print queryByID[id]
1
додано
@Tirthankar: Я тільки що додав версію Python, чи допомагає це? Сценарій очікує два файли file1 та file2 . Він друкує збіги зі стандартним виходом, тому ви можете перенаправити їх у файл.
додано Автор Frerich Raabe, джерело
@Tirthankar ви можете скористатися pastebin замість демпінгу коду в коментарі ... :-)
додано Автор Marcello Romani, джерело
Я також використовував perl, але це не допомогло: my $ input_ri_file = "input_RI.csv"; my $ input_sql_file = "SQL_file.sql"; my $ output_sql_file = "Out_SQL_file.sql"; open (SQLFILE, "<$ input_sql_file"); foreach () {push @ lin, $ _, якщо ($ _ eq "\ t } закрити $ SQLFILE; open (RILIST, "<$ input_ri_file"); foreach () {push @ ri_list, $ _, якщо ($ _ eq "\ t } закрити $ RILIST; open (FINALSQLFILE, "> $ output_sql_file"); foreach $ ri (@ ri_list) {chomp $ ri; для (@ lin) {if ($ _ = ~/$ ri /) {друкувати FINALSQLFILE $ _; }}} закрити FINALSQLFILE;
додано Автор Tirthankar, джерело

Я пропоную використовувати мову програмування, таку як Perl, ruby або Python.

У Ruby, рішення, що читає обидва файли ( f1 та f2 ), може бути:

idxes = File.readlines('f1').map(&:chomp)

File.foreach('f2') do | line |
  next unless line =~ /where ri=(\d+);$/
  puts line if idxes.include? $1
end

або з Perl

open $file, '<', 'f1';
while (<$file>) { chomp; $idxs{$_} = 1; }
close($file);

open $file, '<', 'f2';
while (<$file>) {
    next unless $_ =~ /where ri=(\d+);$/;
    print $_ if $idxs{$1};
}
close $file;
1
додано

Рішення awk/grep, згадані вище, були повільними або голодні пам'яті на моїй машині (file1 10 ^ 6 рядків, file2 10 ^ 7 рядків). Тому я придумав рішення SQL, використовуючи sqlite3.

Turn file2 into a CSV-formatted file where the first field is the value after ri=

cat file2.txt  | gawk -F= '{ print $3","$0 }' | sed 's/;,/,/' > file2_with_ids.txt

Створіть дві таблиці:

sqlite> CREATE TABLE file1(rowId char(10));
sqlite> CREATE TABLE file2(rowId char(10), statement varchar(200));

Імпортувати ідентифікатори рядків з файлу1:

sqlite> .import file1.txt file1

Імпортуйте оператори з файлу2, використовуючи "підготовлену" версію:

sqlite> .separator ,
sqlite> .import file2_with_ids.txt file2

Виділіть усі оператори в таблиці file2 зі відповідним рядком в таблиці file1 :

sqlite> SELECT statement FROM file2 WHERE file2.rowId IN (SELECT file1.rowId FROM file1);

Файл 3 можна легко створити, перенаправляючи вивід до файлу, перш ніж видати оператор select:

sqlite> .output file3.txt

Тестові дані:

sqlite> select count(*) from file1;
1000000
sqlite> select count(*) from file2;
10000000
sqlite> select * from file1 limit 4;
1610666927
1610661782
1610659837
1610664855
sqlite> select * from file2 limit 4;
1610665680|update TABLE_X set ATTRIBUTE_A=87 where ri=1610665680;
1610661907|update TABLE_X set ATTRIBUTE_A=87 where ri=1610661907;
1610659801|update TABLE_X set ATTRIBUTE_A=87 where ri=1610659801;
1610670610|update TABLE_X set ATTRIBUTE_A=87 where ri=1610670610;

Без створення будь-яких індексів, оператор select прийняв близько 15 секунд на машині AMD A8 1.8HGz 64bit Ubuntu 12.04.

1
додано

## reports any lines contained in < file 1> missing in < file 2>

IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
do ((\!$(grep -F -c -- "$a" < file 2>))) && echo $a; 
done && unset IFS

або робити те, що хоче запитувати, зняти заперечення і перенаправити

(IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
do (($(grep -F -c -- "$a" < file 2>))) && echo $a; 
done && unset IFS) >> < file 3> 
1
додано
Ви можете побачити сценарій оболонки, який я нещодавно зробив, щоб зробити це на моєму блозі: scriptsandoneliners. blogspot.com/2014/08/blog-post.html
додано Автор user3897784, джерело

Більшість попередніх відповідей правильні, але для мене працювала тільки ця команда

grep -oi -f a.txt b.txt

enter image description here

0
додано

Можливо, спробуйте AWK і використовуйте номер з файлу 1 як ключ, наприклад, простий скрипт

First script will produce awk script:
awk -f script1.awk

 {
   print "\$0 ~ ",$0,"{ print \$0 }" > script2.awk;
 }

а потім викликати script2.awk з файлом

0
додано
Так, але якщо ви прочитали ваш файл1 один раз і створили один великий регулярний вираз, а потім перевірте всі рядки у файлі2, то це буде прочитано кожний файл тільки один раз.
додано Автор Damian0o, джерело
Але це також спричинить багаторазове зчитування файлів як на файлі 1, так і на файлі 2. Чи не вважаєте ви, що це також буде повільніше.
додано Автор Tirthankar, джерело