Як я перехоплюю декілька шаблонів із малюнком, що має характер труби?


623

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

grep (foo|bar) *.txt

але оболонка інтерпретує |як трубу і скаржиться, коли barвона не виконується.

Як я можу підключитись до декількох шаблонів в одному наборі файлів?


можливий дублікат Grep: як додати умову "АБО"?
phuclv

grep 'word1 \ | word2 \ | word3' / шлях / до / файл
lambodar

Відповіді:


861

Спочатку потрібно захистити візерунок від розширення оболонкою. Найпростіший спосіб зробити це - поставити навколо нього одинарні лапки. Одиночні котирування перешкоджають розширенню будь-чого між ними (включаючи зворотні риски); Єдине, чого ви не можете зробити, це мати одинарні лапки в шаблоні.

grep 'foo*' *.txt

Якщо вам потрібна одна ціна, ви можете записати її як '\''(кінцевий рядковий літерал, буквальна цитата, відкритий рядковий літерал).

grep 'foo*'\''bar' *.txt

По-друге, grep підтримує два синтаксиси для шаблонів. Старий синтаксис за замовчуванням ( базові регулярні вирази ) не підтримує |оператора alternation ( ), хоча деякі версії мають його як розширення, але записуються із зворотною косою рисою.

grep 'foo\|bar' *.txt

Портативний спосіб - використовувати новіші синтаксиси, розширені регулярні вирази . Вам потрібно пройти -Eопцію, grepщоб вибрати його. В Linux ви можете також вводити egrepзамість grep -E(на інших пристроях, ви можете зробити цей псевдонім).

grep -E 'foo|bar' *.txt

Ще одна можливість, коли ви просто шукаєте будь-яку з декількох шаблонів (на відміну від побудови складного шаблону за допомогою диз'юнкції), - це передавати декілька шаблонів grep. Це можна зробити, попередньо перейшовши до кожного шаблону з -eопцією.

grep -e foo -e bar *.txt

18
Як сторонне позначення - коли виправлені візерунки, ви дійсно повинні ввійти в звичку fgrepабо grep -F, для невеликих шаблонів різниця буде незначною, але, коли вони будуть довші, переваги починають проявлятися ...
TC1

7
@ TC1 fgrep застарілий відповідно до сторінки man
ramn

18
@ TC1 Чи grep -Fмає фактична користь від продуктивності, залежить від реалізації grep: деякі з них так чи інакше застосовують той же алгоритм, що -Fзмінює лише час, витрачений на аналіз шаблону, а не час пошуку. -FНаприклад, GNU grep не швидше , наприклад (у нього також є помилка, яка grep -Fуповільнює у багатобайтових локалях - той самий постійний малюнок grepнасправді значно швидший!). З іншого боку, grep BusyBox приносить велику користь від -Fвеликих файлів.
Жиль

4
Можливо, слід зазначити, що для більш складних шаблонів, де чергування має бути лише частиною регулярного виразу, воно може бути згруповане з "\ (" і "\)" (вихідний за замовчуванням "базові регулярні вирази" ) (?).
Пітер Мортенсен

4
Зауважимо, що egrepпопередні grep -E. Він не є специфічним для GNU (це, звичайно, не має нічого спільного з Linux). Насправді ви все ще знайдете такі системи, як Solaris, де за замовчуванням grepвсе ще не підтримується -E.
Стефан Шазелас

89
egrep "foo|bar" *.txt

або

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

вибірково цитуючи чоловічу сторінку gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

На початку я не читав далі, тому не визнавав тонких відмінностей:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

Я завжди використовував egrep і непотрібно parens, тому що я вчився на прикладах. Тепер я дізнався щось нове. :)


22

Як сказав TC1, -Fздається, корисний варіант:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige Я не знав про параметр $ 'foo \ nbar', не знаю, як працює тут розширення, потрібно шукати, але дякую, це справді корисно.
haridsv

Приємно! Цей варіант також здається, що він працює набагато швидше (оскільки він вимикає регулярний вираз).
qwertzguy

15

По-перше, потрібно використовувати лапки для спеціальних символів. По-друге, навіть так, grepне зрозуміє чергування безпосередньо; вам потрібно буде використовувати egrepабо ( grepлише для GNU ) grep -E.

egrep 'foo|bar' *.txt

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


4
Насправді, grep -Eце більше, ніж стандарт egrep.
jw013

8

Якщо вам не потрібні регулярні вирази, це набагато швидше використовувати fgrepабо grep -Fз кількома параметрами -e, як це:

fgrep -efoo -ebar *.txt

fgrep(альтернативно grep -F) набагато швидше звичайного grep, оскільки він шукає виправлені рядки замість регулярних виразів.


4
Будь ласка, дивіться також коментарі на цій сторінці, де згадується, що fgrepзастаріло.
phk

6

Ви можете спробувати команду нижче, щоб отримати результат:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Дешевий та веселий спосіб поздоровитись із кількома моделями:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

Це може отримати користь від пояснення.
Пітер Мортенсен

2
Пояснення полягає в тому, що -fопція grep приймає файл з декількома шаблонами. Замість того, щоб створювати тимчасовий файл (який ви можете забути видалити згодом), просто використовуйте процедуру підстановки оболонки:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Pipe ( |) - це особливий символ оболонки, тому його потрібно або уникати ( \|), або цитувати відповідно до інструкції ( man bash):

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

Замикання символів у подвійних лапках зберігає буквальне значення всіх символів у лапках

Нецитується зворотна косою рисою ( \) є символом втечі.

Дивіться: Які символи потрібно уникнути в Bash?

Ось кілька прикладів (використовуючи інструменти, які ще не були згадані):

  • Використання ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Використання git grep:

    • git grep --no-index -e foo --or -e bar

      Примітка: Це також підтримує логічні вирази , такі як --and, --orі --not.

Щодо операції AND у рядку, див.: Як запустити grep з декількома шаблонами AND?

Про операцію І на файл дивіться у розділі: Як перевірити, чи існують у файлі численні рядки чи регулярні виразки?


3

У мене були журнали доступу, де дати були тупо відформатовані: [30 / черв / 2013: 08: 00: 45 +0200]

Але мені потрібно було відобразити це як: 30 / черв / 2013 08:00:45

Проблема полягає в тому, що, використовуючи "АБО" у своїй заяві Grep, я отримував два вирази відповідності у двох окремих рядках.

Ось таке рішення:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR: якщо ви хочете зробити більше речей після відповідності одному з декількох шаблонів, додайте їх як в \(pattern1\|pattern2\)

Приклад: Я хочу знайти всі місця, де змінна, яка містить ім'я 'date', визначається як String або int. (наприклад, "int cronDate =" або "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

З grep -E, вам не потрібно уникати дужок чи труби, тобтоgrep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Це працює для мене

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

Існує кілька способів зробити це.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

3-й і 4-й варіант буде відображатись лише у файлах і уникати каталогів, що мають .txtїх імена.
Отже, відповідно до вашого випадку використання, ви можете скористатися будь-яким із варіантів, згаданих вище.
Дякую!!


0

щоб додати до відповіді @ geekosaur , якщо у вас є кілька шаблонів, які також містять вкладки та пробіл, ви використовуєте наступну команду

grep -E "foo[[:blank:]]|bar[[:blank:]]"

де [[:blank:]]клас символів RE, який представляє або пробіл, або символ вкладки

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.