我对 grep 正则表达式中需要多少个反斜杠来转义交替运算符
|
有点困惑。这个
echo abcdef | grep -e"def|zzz"
不输出任何内容,因为 grep 不处于扩展正则表达式模式。用一个反斜杠转义是可行的,
echo abcdef | grep -e"def\|zzz"
打印
abcdef
。更令人惊讶的是,用 2 个反斜杠转义也可以,
echo abcdef | grep -e"def\\|zzz"
打印
abcdef
。用三个反斜杠转义失败,
echo abcdef | grep -e"def\\\|zzz"
不打印任何内容。
有人有解释吗,特别是对于 2 个反斜杠的情况?
编辑:
使用这个简单的参数打印程序,
void main(int argc, char** argv)
{
for (int i = 0; i < argc; i++)
printf("Arg %d: %s\n", i, argv[i]);
}
我研究了我的 shell 使用上面的命令行做了什么:
-e"def|zzz"
变成 -edef|zzz
-e"def\|zzz"
变成 -edef\|zzz
-e"def\\|zzz"
变成 -edef\\|zzz
-e"def\\\|zzz"
变成 -edef\\\|zzz
因此,所有双引号都被删除,并且反斜杠和管道不会被 shell 更改。我怀疑 grep 本身对文字字符串做了一些特殊的事情
\\|
。
小写的
-e
选项用于表示多个搜索操作。隐含的交替是:
$ echo abcdef | grep -e 'def' -e'zzz'
abcdef
$ echo abczzz | grep -e 'def' -e'zzz'
abczzz
或者,您可以使用上面的
-E
选项来扩展正则表达式表示法:
$ echo abcdef | grep -E 'def|zzz'
abcdef
我相信这可以直接解决您的问题(使用
-e
进行交替或 -E
进行扩展正则表达式表示法)。希望这有帮助:-)
FWIW,反斜杠的问题是
|
对 bash 有特殊含义,需要转义,除非它在单引号中。以下是有关引用和转义规则以及常见陷阱的资源:http://wiki.bash-hackers.org/syntax/quoting
如果你双引号你的正则表达式,shell 特别对待反斜杠(强调我的):
仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:
、$
、`
、"
或\
。在双引号内,后面跟着这些字符之一的反斜杠将被删除。newline
这意味着您的表达将被如下处理:
grep -e"def|zzz"
– grep 接收 def|zzz
;因为它默认为基本正则表达式 (BRE),所以 |
并不特殊1,并且 grep 会尝试匹配文字字符串 def|zzz
。grep -e"def\|zzz"
– |
不是上面提到的特殊字符之一,因此 grep 接收 def\|zzz
,GNU grep 将 \|
视为替代1。grep -e"def\\|zzz"
– 根据手册摘录,\\
是特殊的(尝试echo "\\"
); grep 看到 def\|zzz
因为 shell 删除了反斜杠,并且行为与第二种情况相同。grep -e"def\\\|zzz"
– shell 将其转换为 def\\|zzz
(\\
变为 \
,\|
对于 shell 来说并不特殊,保持不变); grep 将 \\
视为文字反斜杠(反斜杠由反斜杠转义),因此 |
并不特殊,并且 grep 尝试匹配确切的字符串 def\|zzz
。一般来说,谨慎的做法是用单引号引用你的正则表达式,这样 shell 就不会打扰它。
顺便说一句,我不认为你的 C 程序代表了 shell 如何处理参数;在Shell操作中,引用是一个单独的步骤,包括反斜杠处理(请参阅转义字符)。
1作为扩展,GNU grep 允许您在 BRE 中转义
|
并获得交替。 POSIX BRE 没有交替。因此,GNU grep 的 grep
和 grep -E
之间的唯一区别是必须转义的内容;功能是相同的。
第一个失败,因为 grep 以编程方式转义管道,导致正则表达式中出现literal管道。
最后一次尝试失败,因为
\\\|
会导致 literal 反斜杠,然后在正则表达式中产生 literal 管道。
echo 'def|zzz' | grep -e "def|zzz" --> def|zzz
echo 'def\\|zzz' | grep -e "def\\\|zzz" --> def\|zzz
根据 grep 手册页,特别是根据信息页,为 grep 给出的所有示例都包含单引号而不是双引号。
用单引号进行一些类似的测试,我们有不同且正确的行为:
$ cat file1
def
def\
def\\
def\\\
def\|
aaa
nnn
$ cat -n file1 |grep -e 'def|zzz' #No results
$ cat -n file1 |grep -e 'def\|zzz'
1 def
2 def\
3 def\\
4 def\\\
5 def\|
$ cat -n file1 |grep -e 'def\\|zzz' #No results
$ cat -n file1 |grep -e 'def\\\|zzz'
2 def\
3 def\\
4 def\\\
5 def\|
$ cat -n file1 |grep -e 'def\\\\|zzz' #No results
$ cat -n file1 |grep -e 'def\\\\\|zzz'
3 def\\
4 def\\\
结论:对于 grep 中的正则表达式,请使用单引号。
但说实话,我不知道为什么使用双引号时行为完全不同。应该与 bash 扩展有关。
更新
查看这个 bash 函数测试结果,证明了 args 中单引号和双引号的不同解释:
function tt { printf "%s: %s\n" "$1" "$2"; }
tt -e 'def\\|aaa' #Parsed correctly
tt -e 'def\\\|aaa' #We send three slashes - function gets three slashes
tt -e 'def\\\\|aaa' #We send four slashes - function gets four slashes
tt -e "def\\|aaa" #We send two slashes but function displays ONE
tt -e "def\\\|aaa" #We send three slashes but function displays TWO
tt -e "def\\\\|aaa" #We send four slashes but function displays TWO
#Output
-e: def\\|aaa
-e: def\\\|aaa
-e: def\\\\|aaa
-e: def\|aaa
-e: def\\|aaa
-e: def\\|aaa
注意双引号内有三个和四个斜杠的情况。
再多一步:
tt -e 'def\|aaa' #Displays def\|aaa (correct parsing)
tt -e 'def\\|aaa' #Displays def\\|aaa (correct parsing)
tt -e "def\|aaa" #Displays def\|aaa (correct parsing)
tt -e "def\\|aaa" #Displays def\|aaa (same as before - not correct parsing)
上面双引号中的最后两行可能解释了为什么测试结果(
\|
vs \\|
)在用双引号括起来时具有相同的正则表达式操作。