我如何grep多个带有管道字符的模式?


623

我想在几个文件中找到与两种模式之一匹配的所有行。我试图通过键入找到想要的模式

grep (foo|bar) *.txt

但是外壳程序将解释|为管道,并在bar不是可执行文件时进行投诉。

如何在同一组文件中对多个模式进行grep?



grep'word1 \ | word2 \ | word3'/ path / to / file
lambodar

Answers:


861

首先,您需要保护模式以免被外壳扩展。最简单的方法是用单引号引起来。单引号可以防止它们之间的任何内容扩展(包括反斜杠);您唯一不能做的就是在模式中使用单引号。

grep 'foo*' *.txt

如果确实需要单引号,则可以将其写为'\''(结束字符串文字,文字引号,开放字符串文字)。

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

其次,grep支持两种模式语法。旧的默认语法(基本正则表达式)不支持alteration(|)运算符,尽管某些版本将其作为扩展名,但使用反斜杠编写。

grep 'foo\|bar' *.txt

可移植的方法是使用更新的语法,扩展的正则表达式。您需要传递-E选项将grep其选中。在Linux上,您也可以键入egrep代替grep -E(在其他unice上,您可以将其设为别名)。

grep -E 'foo|bar' *.txt

当您只是寻找几种模式中的任何一种时(相对于使用析取来构建复杂模式的另一种可能)是将多个模式传递给grep。您可以通过在每个模式之前添加该-e选项来实现。

grep -e foo -e bar *.txt

18
附带说明-固定模式后,您应该真正养成fgrep或的习惯grep -F,对于小的模式,差异可以忽略不计,但是随着时间的延长,好处开始显现出来……
TC1 2012年

7
@ TC1 fgrep根据手册页已弃用
ramn

18
@ TC1是否grep -F具有实际的性能优势取决于grep的实现:它们中的某些无论如何都应用相同的算法,因此,这-F仅对解析模式所花费的时间有所不同,而与搜索时间无关。例如,GNU grep的运行速度并不快-F(它还有一个使grep -F多字节语言环境变慢的错误-相同的常量模式grep实际上快得多!)。另一方面,BusyBox grep确实从-F大文件中受益匪浅。
吉尔斯2014年

4
也许应该提到的是,对于仅用于正则表达式一部分的更复杂的模式,可以将其与“ \(”和“ \””分组(转义用于默认的“基本正则表达式” )(?)。
彼得·莫滕森

4
需要注意的是egrepgrep -E。它不是特定于GNU的(与Linux毫无关系)。实际上,您仍然会发现像Solaris这样的系统,其中的默认值grep仍然不支持-E
斯特凡Chazelas

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 2012年

真好!这个选项似乎也使其运行得更快(因为它禁用了正则表达式)。
qwertzguy

15

首先,您需要对特殊字符使用引号。其次,即使这样,grep也不会直接理解交替。您将需要使用egrep,或(grep仅适用于GNU )grep -E

egrep 'foo|bar' *.txt

(除非是大正则表达式的一部分,否则括号是不必要的。)


4
实际上,grep -E它比更为标准egrep
2012年

8

如果您不需要正则表达式,则使用它fgrepgrep -F使用多个-e参数会更快,如下所示:

fgrep -efoo -ebar *.txt

fgrep(或者grep -F)比常规grep快得多,因为它搜索固定字符串而不是正则表达式。


4
另请参阅此页面上fgrep已过时的提及评论。
phk

6

您可以尝试以下命令来获得结果:

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

3

一种便宜而开朗的grep多种模式的方法:

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

可以从解释中受益。
彼得·莫滕森

2
原因是grep的-f选项采用了具有多个模式的文件。无需创建临时文件(您可能会忘记随后删除该文件),而只需使用Shell的进程替换即可:grep -f <(echo foo; echo bar) *.txt
Jakob,

3

竖线(|)是特殊的外壳字符,因此需要转义(\|)或按手册引用(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操作,请参见:如何使用多个AND模式运行grep?

有关每个文件的AND操作,请参阅:如何检查文件中是否存在所有多个字符串或正则表达式?


3

我有访问日志,其中的日期格式很愚蠢:[30 / Jun / 2013:08:00:45 +0200]

但我需要将其显示为:30 / Jun / 2013 08:00:45

问题是在我的grep语句中使用“ OR”时,我在两条单独的行上收到了两个match表达式。

解决方法如下:

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个选项将仅在文件中使用grep,并避免.txt名称中包含目录。
因此,根据您的用例,您可以使用上述任何选项。
谢谢!!


0

要添加到@geekosaur的答案中,如果您有多个也包含制表符和空格的模式,请使用以下命令

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

[[:blank:]]RE字符类在哪里,代表空格或制表符

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.