Получить шаблоны в одном файле из другого, используя ack или awk или лучший способ, чем grep?

Есть ли способ получить шаблоны в одном файле (список шаблонов) из другого файла, используя ack как параметр -f в grep? Я вижу, что в ack есть опция -f, но она отличается от -f в grep.

Возможно, пример даст вам лучшее представление. Предположим, у меня есть файл1:

file1:
a
c
e

И файл2:

file2:
a  1
b  2
c  3
d  4
e  5

И я хочу получить все шаблоны в файле1 из файла2, чтобы дать:

a  1
c  3
e  5

ack может это сделать? В противном случае, есть ли лучший способ справиться с заданием (например, awk или с использованием хэша), потому что у меня есть миллионы записей в обоих файлах, и мне действительно нужен эффективный способ завершения? Спасибо!


person Rock    schedule 30.03.2012    source источник
comment
Является ли использование Ack для этого абсолютным требованием или доступны и другие инструменты? Как grep терпит неудачу? И действительно ли шаблоны в файле1 являются регулярными выражениями или это просто строки для сопоставления?   -  person ghoti    schedule 30.03.2012
comment
grep на самом деле довольно эффективен...   -  person Amber    schedule 30.03.2012
comment
Привет, Готи, спасибо, что спросил. На самом деле Ack НЕ требуется. Просто я ищу более быстрый способ, чем grep, так как работа с миллионами записей с использованием grep действительно утомительна. File1 может быть либо регулярным выражением, либо строкой. Я просто хочу, чтобы это было быстро. Вы случайно не знаете лучшие инструменты? Спасибо!   -  person Rock    schedule 30.03.2012
comment
Работа с миллионами записей будет проблемой для большинства инструментов. Вы можете только передавать и анализировать данные так быстро. Обратите внимание, однако, что если то, что вы ищете, является фиксированными строками (а не фактическими регулярными выражениями), fgrep будет быстрее, чем обычное grep, поскольку оно не будет вызывать механизм регулярных выражений.   -  person Amber    schedule 30.03.2012
comment
@Amber Можете ли вы дать больше подсказок? Как быстро стримить и парсить?   -  person Rock    schedule 30.03.2012
comment
Сколько времени у вас занимает grep? Я не говорил, что знаю секрет, как заставить его работать быстрее, я говорил, что обычно, если grep работает медленно, когда вы анализируете только один файл стога сена, вы, вероятно, не найдете более быстрого варианта.   -  person Amber    schedule 30.03.2012
comment
Но fgrep идентичен grep -F на большинстве платформ. Я поэтому и спросил выше. Если вы можете ограничить свой файл1 строками, а не регулярными выражениями, grep может быть наиболее эффективным инструментом, который вы можете найти, не создавая его с нуля самостоятельно.   -  person ghoti    schedule 30.03.2012
comment
Черт возьми, если у вас достаточно оперативной памяти, вы можете написать небольшой скрипт awk, который будет загружать файл1 в индекс массива, а затем сопоставлять его с поиском в массиве. Не знаю, будет ли это быстрее, чем grep, но это то, что вы можете сравнить для сравнения с grep, используя подмножество ваших данных.   -  person ghoti    schedule 30.03.2012
comment
Как вы думаете, где grep -F тратит циклы? (подсказка; блокировка ввода из вашего источника данных.) Вы не привязаны к процессору.   -  person DavidO    schedule 30.03.2012
comment
Сейчас я пробую %hash и надеюсь, что это будет более экологично.   -  person Rock    schedule 30.03.2012
comment
Итак, этот вопрос касается awk, верно, а не какого-то нового языка под названием ack?   -  person maerics    schedule 30.03.2012
comment
Считается, что № Ack работает быстрее, чем grep. Посетите betterthangrep.com.   -  person Rock    schedule 30.03.2012
comment
Я считаю, что он считается более быстрым при поиске исходных деревьев, потому что он игнорирует каталоги VCS. Вы должны оценить его производительность в вашем случае, потому что я подозреваю, что все, что написано на C (например, grep), будет работать быстрее, чем то же самое, написанное на интерпретируемом языке, таком как Perl.   -  person ghoti    schedule 30.03.2012
comment
Ускорение ack заключается не только в игнорировании каталогов VCS, но и в игнорировании файлов, которые не являются исходным кодом. Разница в скорости C/Perl минимальна, потому что регулярные выражения Perl высоко оптимизированы, и в любом случае вы в основном связаны с вводом-выводом.   -  person Andy Lester    schedule 12.04.2012
comment
Прошло несколько лет, и следующим важным событием в поиске стал Серебряный искатель. Проверьте geoff.greer.fm/ag и betterthanack.com для получения подробной информации.   -  person ghoti    schedule 25.04.2017


Ответы (5)


Вот однострочник Perl, который использует хэш для хранения набора нужных ключей из файла1 за O(1) (амортизированное время) поиска за итерацию по строкам файла2. Таким образом, он будет выполняться за время O(m+n), где m — количество строк в вашем наборе ключей, а n — количество строк в тестируемом файле.

perl -ne'BEGIN{open K,shift@ARGV;chomp(@a=<K>);@hash{@a}=()}m/^(\p{alpha}+)\s/&&exists$hash{$1}&&print' tkeys file2

Набор ключей будет храниться в памяти, пока файл2 будет проверяться построчно на соответствие ключам.

Вот то же самое с использованием параметра командной строки Perl -a:

perl -ane'BEGIN{open G,shift@ARGV;chomp(@a=<G>);@h{@a}=();}exists$h{$F[0]}&&print' tkeys file2

Вторая версия, вероятно, немного проще для глаз. ;)

Здесь вы должны помнить одну вещь: более вероятно, что вы привязаны к вводу-выводу, а не к процессору. Таким образом, цель должна состоять в том, чтобы свести к минимуму использование ввода-вывода. Когда весь набор ключей поиска содержится в хэше, который предлагает O (1) амортизированных поисков. Преимущество этого решения по сравнению с другими решениями заключается в том, что некоторым (более медленным) решениям придется запускать ваш ключевой файл (файл1) один раз для каждой строки файла2. Такое решение будет O(m*n), где m — размер вашего ключевого файла, а n — размер файла2. С другой стороны, этот хэш-подход обеспечивает время O(m+n). Это величина разницы. Его преимущество заключается в устранении линейного поиска по набору ключей и дополнительных преимуществах в том, что ключи считываются через ввод-вывод только один раз.

person DavidO    schedule 30.03.2012
comment
Сравнил hash с использованием Perl с awk. Должен сказать, что hash просто супер быстрее, чем awk и даже grep. - person Rock; 31.03.2012
comment
У меня сработала только вторая версия, но она работала и была намного быстрее, чем любое другое решение, которое я пробовал. - person FBB; 17.07.2014

Ну ладно, если мы перешли от комментариев к ответам... ;-)

Вот однострочник awk, который делает то же самое, что и однострочник DavidO на perl, но в awk. Awk меньше и, возможно, компактнее, чем Perl. Но есть несколько разных реализаций awk. Я понятия не имею, будет ли ваш работать лучше, чем другие, или чем perl. Вам нужно будет провести бенчмаркинг.

awk 'NR==FNR{a[$0]=1;next} {n=0;for(i in a){if($0~i){n=1}}} n' file1 file2

Что (должно) это делать?

Первая часть awk-скрипта сопоставляет только строки в файле file1 (где номер записи в текущем файле равен общему количеству записей) и заполняет массив. Вторая часть (которая выполняется в последующих файлах) проходит по каждому элементу в массиве и проверяет, можно ли его использовать в качестве регулярного выражения для соответствия текущей входной строке.

Второй блок кода начинается с «n», который в предыдущем блоке был установлен либо на 0, либо на 1. В awk «1» оценивается как истина, а отсутствующий блок фигурных скобок считается эквивалентным {print}, поэтому, если предыдущий блок нашел совпадение, этот выведет текущую строку.

Если файл1 содержит строки вместо регулярных выражений, вы можете изменить это, чтобы он работал быстрее, заменив первое сравнение на if(index($0,i))....

Используйте с осторожностью. Ваш пробег может отличаться. Создан на объекте, который может содержать орехи.

person ghoti    schedule 30.03.2012
comment
Спасибо. Но это печатает первую строку в файле2 три раза, как в примере выше: a 1 a 1 a 1 - person Rock; 30.03.2012
comment
Плохо, это сравнивалось со значением массива вместо индекса. Я исправил код, немного переработал его и даже протестировал. А теперь короче! - person ghoti; 30.03.2012
comment
Вы можете изменить 2-ю часть вашего awk-скрипта с {n=0;for(i in a){if($0~i){n=1}}} n на {for (i in a) if ($0 ~ i) {print; break}} — используйте «break», чтобы остановить цикл «for», как только вы нашли совпадение, и явно используйте «print» для удобочитаемости. - person glenn jackman; 30.03.2012
comment
@glennjackman - хороший звонок, это может быть заметной оптимизацией, если массив действительно большой. Он также устраняет переменную n, что мне нравится. - person ghoti; 30.03.2012

nawk 'FNR==NR{a[$0];next}($1 in a)' file3 file4

проверено:

pearl.384> cat file3
a
c
e
pearl.385> cat file4
a  1 
b  2 
c  3 
d  4 
e  5
pearl.386> nawk 'FNR==NR{a[$0];next}($1 in a)' file3 file4
a  1 
c  3 
e  5
pearl.387>
person Vijay    schedule 30.03.2012
comment
Если это вообще работает, оно соответствует только в том случае, если файл1 и файл2 имеют идентичные строки, а не если файл1 содержит подстроки или регулярные выражения для сопоставления, как в примере данных OP. Вы проверяли это? - person ghoti; 30.03.2012
comment
да, не увидел вопрос должным образом. исправил. - person Vijay; 30.03.2012
comment
Я вижу твое исправление. Хотя он работает с данными примера, я не уверен, насколько хорошо он будет обрабатывать общие случаи. В любом случае минус удален. - person ghoti; 30.03.2012

TXR может быть еще одним вариантом выполнения ваших требований. Я слишком новичок в этом, чтобы писать то, что вам нужно, но автор часто участвует в StackOverflow. Хотя я уверен, что вы можете делать то, что вам нужно с TXR, но я не уверен, что он будет работать лучше. Вам нужно будет протестировать.

Стоит посмотреть, если вас интересует целый язык, посвященный сопоставлению с образцом. :)

person ghoti    schedule 30.03.2012
comment
Почему бы вам не добавить это к своему предыдущему ответу? Странно, что один и тот же человек дает два ответа на один вопрос!! - person Vijay; 30.03.2012
comment
Это не часть предыдущего ответа. Это было ахуенно. Это отсылка к TXR. Совершенно разные. Конечно, без фактического кода он не заслуживает одобрения, но как вы думаете, какую дезинформацию он дает? - person ghoti; 30.03.2012
comment
не дает никакой дезинформации, но не вредит добавлению этого к вашему предыдущему ответу. но точно не в качестве второго ответа! вы всегда можете добавить в качестве второго варианта для первого ответа. - person Vijay; 30.03.2012
comment
Спасибо за предложение. В любом случае это может быть потенциальным решением. Дал голос, чтобы облегчить ссору. - person Rock; 30.03.2012
comment
@peter, нет ничего плохого в том, чтобы ответить дважды, если ответы разные - person glenn jackman; 30.03.2012
comment
@peter - если ОП решит использовать TXR для решения своей проблемы, как вы думаете, было бы уместно, если бы он выбрал в качестве лучшего ответа тот, который больше всего сосредоточен на AWK? Нет. Разные решения должны публиковаться как разные ответы. - person Graham; 30.03.2012

Вы можете преобразовать файл в регулярное выражение для подтверждения с помощью tr. Я использовал sed для удаления символа завершающей трубы.

ack "`tr '\n' '|' ‹ patts | sed 's/.$//'`"

Обратите внимание, что для этого вам нужна пара процессов, поэтому решение awk, вероятно, более эффективно, но его довольно легко запомнить.

person Captain Lepton    schedule 20.06.2013