Dan*_*Dan 764 shell grep quoting regular-expression
我想在多个文件中找到与两种模式之一匹配的所有行。我试图通过键入来找到我正在寻找的模式
grep (foo|bar) *.txt
Run Code Online (Sandbox Code Playgroud)
但是外壳将它解释|为管道并在bar不是可执行文件时抱怨。
如何在同一组文件中搜索多个模式?
Gil*_*il' 1034
首先,您需要保护模式不被外壳扩展。最简单的方法是在它周围加上单引号。单引号防止它们之间的任何扩展(包括反斜杠);你唯一不能做的就是在模式中使用单引号。
grep -- 'foo*' *.txt
Run Code Online (Sandbox Code Playgroud)
(另请注意--选项结束标记,以阻止grep包括 GNU 在内的某些实现grep将调用的文件-foo-.txt(将被 shell 从 展开*.txt)视为选项(即使它遵循非选项参数)这里))。
如果确实需要单引号,则可以将其写为'\''(结束字符串文字、文字引号、开放字符串文字)。
grep -- 'foo*'\''bar' *.txt
Run Code Online (Sandbox Code Playgroud)
其次,grep 至少支持¹ 两种模式语法。旧的默认语法(基本正则表达式)不支持交替 ( |) 运算符,尽管某些版本将其作为扩展名,但用反斜杠编写。
grep -- 'foo\|bar' *.txt
Run Code Online (Sandbox Code Playgroud)
可移植的方式是使用较新的语法、扩展的正则表达式。您需要将-E选项传递grep给选择它(以前是用egrep单独的命令²完成的)
grep -E -- 'foo|bar' *.txt
Run Code Online (Sandbox Code Playgroud)
当您只是在寻找几种模式中的任何一种(而不是使用析取构建复杂模式)时,另一种可能性是将多个模式传递给grep. 您可以通过在每个模式前面加上-e选项来做到这一点。
grep -e foo -e bar -- *.txt
Run Code Online (Sandbox Code Playgroud)
或者将模式放在几行上:
grep -- 'foo
bar' *.txt
Run Code Online (Sandbox Code Playgroud)
或者将这些模式存储在一个文件中,每行一个并运行
grep -f that-file -- *.txt
Run Code Online (Sandbox Code Playgroud)
请注意,如果*.txt扩展为单个文件,grep则不会像存在多个文件时那样在匹配行前面加上其名称。为了解决这个问题,使用grepGNU 等一些实现grep,您可以使用该-H选项,或者对于任何实现,您可以将其/dev/null作为额外参数传递。
¹ 一些grep实现甚至支持更像与 perl 兼容的那些-P,或者增强的那些-X,-K用于 ksh 通配符......
²而egrep已经被弃用POSIX,有时也不再在某些系统中,当上尚未安装的POSIX或GNU工具其它一些系统,如Solaris,然后egrep是你作为其唯一的选择/bin/grep支持没有的-e,-f,-E,\|或者多线条图案
use*_*own 134
egrep "foo|bar" *.txt
Run Code Online (Sandbox Code Playgroud)
或者
grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt
Run Code Online (Sandbox Code Playgroud)
有选择地引用 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.)
Run Code Online (Sandbox Code Playgroud)
(……)
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.
Run Code Online (Sandbox Code Playgroud)
一开始我没有进一步阅读,所以我没有意识到细微的区别:
Basic vs Extended Regular Expressions
In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead use the
backslashed versions \?, \+, \{, \|, \(, and \).
Run Code Online (Sandbox Code Playgroud)
我总是使用 egrep 和不必要的括号,因为我是从例子中学到的。现在我学到了一些新东西。:)
ДМИ*_*КОВ 27
就像 TC1 说的,-F似乎是可用的选项:
$> cat text
some text
foo
another text
bar
end of file
$> patterns="foo
bar"
$> grep -F "${patterns}" text
foo
bar
Run Code Online (Sandbox Code Playgroud)
gee*_*aur 17
首先,您需要对特殊字符使用引号。其次,即便如此,grep也不会直接理解交替;您需要使用egrep, or (grep仅限GNU )grep -E。
egrep 'foo|bar' *.txt
Run Code Online (Sandbox Code Playgroud)
(除非交替是更大正则表达式的一部分,否则括号是不必要的。)
小智 10
如果您不需要正则表达式,使用fgrep或grep -F使用多个 -e 参数会快得多,如下所示:
fgrep -efoo -ebar *.txt
Run Code Online (Sandbox Code Playgroud)
fgrep(或者grep -F)比常规 grep 快得多,因为它搜索固定字符串而不是正则表达式。
小智 6
您可以尝试以下命令来获得结果:
egrep 'rose.*lotus|lotus.*rose' some_file
Run Code Online (Sandbox Code Playgroud)
管道符 ( |) 是一个特殊的 shell 字符,因此需要对其进行转义 ( \|) 或按照手册引用 ( man bash):
引用用于删除某些字符或单词对 shell 的特殊含义。它可用于禁用对特殊字符的特殊处理,以防止保留字被识别为此类,并防止参数扩展。
将字符括在双引号中可保留引号内所有字符的字面值
不带引号的反斜杠 (
\) 是转义字符。
请参阅:Bash 中哪些字符需要转义?
以下是一些示例(使用尚未提及的工具):
使用ripgrep:
rg "foo|bar" *.txtrg -e foo -e bar *.txt使用git grep:
git grep --no-index -e foo --or -e bar
注意:它还支持布尔表达式,例如--and,--or和--not。
对于每行 AND 操作,请参阅:如何使用多个 AND 模式运行 grep?
对于每个文件的 AND 操作,请参阅:如何检查文件中是否存在所有多个字符串或正则表达式?