Разве эффективнее выполнить grep дважды или использовать регулярное выражение один раз? - PullRequest
3 голосов
/ 18 мая 2011

Я пытаюсь проанализировать пару файлов размером более 2 ГБ и хочу выполнить grep на нескольких уровнях.

Скажем, я хочу получить строки, содержащие "foo" и строки, которые также содержат "bar".

Я мог бы сделать grep foo file.log | grep bar, но меня беспокоит то, что запускать его дважды будет дорого.

Было бы выгодно использовать что-то вроде grep -E '(foo.*bar|bar.*foo)' вместо?

Ответы [ 4 ]

2 голосов
/ 18 мая 2011

Теоретически, самый быстрый способ должен быть:

grep -E '(foo.*bar|bar.*foo)' file.log

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

2 голосов
/ 18 мая 2011

grep -E '(foo|bar)' найдет строки, содержащие 'foo' ИЛИ 'bar'.

Вы хотите строки, содержащие ОБА 'foo' И 'bar'. Любая из этих команд подойдет:

sed '/foo/!d;/bar/!d' file.log

awk '/foo/ && /bar/' file.log

Обе команды - теоретически - должен быть намного более эффективным, чем ваша cat | grep | grep конструкция, потому что:

  • Оба sed и awk выполняют свои собственные чтения файлов; нет необходимости в трубе над головой
  • «Программы», которые я дал sed и awk выше, используют логическое короткое замыкание для быстрого пропуска строк, не содержащих «foo», таким образом проверяя только строки, содержащие «foo», в / bar / regex

Однако я их не проверял. YMMV:)

1 голос
/ 18 мая 2011

Эти две операции принципиально различны.Это:

cat file.log | grep foo | grep bar

ищет foo в file.log, затем ищет bar в любом последнем выводе grep.Принимая во внимание, что cat file.log | grep -E '(foo|bar)' ищет либо foo, либо bar в file.log.Вывод должен быть очень разным.Используйте любое поведение, которое вам нужно.

Что касается эффективности, то они не очень сопоставимы, потому что они делают разные вещи.Оба должны быть достаточно быстрыми.

0 голосов
/ 18 мая 2011

Если вы делаете это:

cat file.log | grep foo | grep bar

Вы печатаете только строки, которые содержат foo и bar в любом порядке.Если это ваше намерение:

grep -e "foo.*bar" -e "bar.*foo" file.log

Будет более эффективным, так как мне нужно только проанализировать вывод.

Обратите внимание, мне не нужен cat, который более эффективен всам.Вам редко понадобится cat, если вы не объединяете файлы (что и является целью команды).В 99% случаев вы можете либо добавить имя файла в конец первой команды в конвейере, либо если у вас есть такая команда, как tr, которая не позволяет вам использовать файл, вы всегда можете перенаправить вводвот так:

tr `a-z` `A-Z` < $fileName

Но хватит о бесполезных cat с.У меня их дома два.

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

fgrep "foo" file.log | fgrep "bar"

В отличие от grep, fgrep не анализирует регулярные выражения, что означает, что он может анализировать строки намного, намного быстрее,Попробуйте это:

time fgrep "foo" file.log | fgrep "bar"

и

time grep -e "foo.*bar" -e "bar.*foo" file.log

И посмотрите, что быстрее.

...