Сравните awk и grep

Сравните awk и grep

Я выполнил две команды ниже для очень больших файлов

grep -E 'string1|string2' 151103*.log|grep 'string3' | grep string4

awk '/string1|string2/ && /string3/ && /string4/' 151103*.log

Выполнение заняло почти столько же времени. Но awkгораздо быстрее показал мне результаты, которые совпали. greptoo показал мне тот же результат, но в конце, когда процесс завершился.

Оба процесса заняли одинаковое время, просто хотелось бы узнать логику, лежащую в основе обоих поисков awkи grep.

Почему awkбыстрее? У обеих программ разная логика поиска? А если я перемешаю строки в поиске выше, повлияет ли это на скорость поиска?

решение1

GNU grepбуферизует вывод, а GNU awk— нет. И даже если бы вы не использовали GNU, awkа использовали какой-то другой вариант, он, скорее всего, все равно был бы построчно буферизован, если бы вы печатали на терминале, и поэтому сбрасывал бы вывод для каждой встречающейся \newline, но ваши grepзаписи в конвейер и поэтому в любом случае блокировали бы буфер. Если у вас есть GNU, grepвы можете использовать grep --line-buffered ... | grep ...его для сравнения, чтобы увидеть результаты так же быстро. Скорее всего, grepпревзойдет awkпрактически любые тесты на соответствие — особенно GNU grep.

Вот как sedможно сделать то, что вы хотите:

sed -ne'/string4/{/string3/s/string[12]/&/p;}' <in >out

решение2

Конвейер grep не может ничего вывести, пока финальная строка grepдля string4 не совпадет с чем-то, и он получает свой ввод только после заполнения предыдущего буфера конвейера. См. связанные вопросыКаков размер буфера трубы?иОтключить буферизацию в канале.

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

решение3

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

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

Если у вас есть один файл из 1000 строк, и только строка 5 соответствует всем трем регулярным выражениям, команда awk выдаст вам вывод после обработки 5-й строки, перед обработкой 6-й строки. Сравните это с конвейерными операторами grep. Первый вызов grep найдет 5-ю строку и любые другие строки, которые могут соответствовать 1-му регулярному выражению, и после обработки 1000-й (последней) строки ввода ее вывод станет вводом для 2-го вызова grep. Второй вызов grep обрабатывает столько строк, сколько есть в 1-м выводе, и выводит строки, которые соответствуют как 1-му, так и 2-му регулярному выражению, которые затем становятся вводом для 3-го вызова grep. Поскольку 3-й вызов grep обрабатывает каждую строку, он выведет любую строку, которая соответствует его регулярному выражению.

Вы можете сравнить лучшие и худшие случаи grep для приведенного выше примера: если ни одна из строк не соответствует ни одному из регулярных выражений, кроме строки 5, которая соответствует всем 5, то первый grep обрабатывает 1000 строк, второй grep обрабатывает 1 строку, а третий grep обрабатывает 1 строку: он обработает 1002 строки, прежде чем получит какой-либо вывод (лучший случай). Если все строки соответствуют первым двум регулярным выражениям, но только одна строка соответствует третьему регулярному выражению, то конвейерная конструкция grep обработает 1000 + 1000 строк + 5 = 2005 строк, прежде чем найдет совпадение на 5-й строке и получит какой-либо вывод (он продолжит обрабатывать оставшиеся 995 строк из вывода второго grep, но вы больше не увидите вывода, потому что больше ничего не совпадет).

Сравните это с командой awk, которая проверяет все три регулярных выражения одновременно для каждой строки и выдает вам вывод после обработки 5-й строки. Разница будет преувеличена, если вы проверите больше файлов одновременно.

Например, сравните, будет ли вывод выполняться быстрее, если вместо одновременного запуска команды grep для всех файлов, как вы сделали выше (теоретически так и должно быть, но результаты могут различаться в зависимости от распределения попаданий по вашим файлам):

grep -E 'string1|string2' 151103*.log|grep 'string3' | grep string4

Вместо этого вы запускаете серию команд grep для каждого файла по отдельности, например так:

for i in 151103*.log; 
  do grep -E 'string1|string2' $i |grep 'string3' | grep string4; 
done

Это все еще не даст такого быстрого вывода, как оператор awk, но вы можете увидеть разницу.

решение4

Хотя grep, awk и sed можно использовать для решения схожих задач, у каждого из них есть свои сильные и слабые стороны.

Awk лучше всего подходит для табличных данных или когда вам необходимо выполнить вычисления и т. д.

Sed отлично справляется с заменой текста.

Grep лучше всего подходит для выбора строк из входных данных, поэтому я ожидал, что он будет быстрее, чем awk для этой задачи. Возможно, если объединить 3 команды grep в одну, то это то, что вы увидите. Сейчас grep находится в невыгодном положении, так как ему нужно запуститься 3 раза, а второй и третий раз нужно ждать ввода от первого. Это может объяснить, почему результат приходит с задержкой. Хотя я в этом не уверен.

Связанный контент