grep 中的转义字符

问题描述 投票:0回答:4

我对 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 本身对文字字符串做了一些特殊的事情

\\|

regex grep
4个回答
7
投票

小写的

-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


3
投票

如果你双引号你的正则表达式,shell 特别对待反斜杠(强调我的):

仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:

$
`
"
\
newline
。在双引号内,后面跟着这些字符之一的反斜杠将被删除

这意味着您的表达将被如下处理:

  1. grep -e"def|zzz"
    – grep 接收
    def|zzz
    ;因为它默认为基本正则表达式 (BRE),所以
    |
    并不特殊1,并且 grep 会尝试匹配文字字符串
    def|zzz
  2. grep -e"def\|zzz"
    |
    不是上面提到的特殊字符之一,因此 grep 接收
    def\|zzz
    ,GNU grep 将
    \|
    视为替代1
  3. grep -e"def\\|zzz"
    – 根据手册摘录,
    \\
    是特殊的(尝试
    echo "\\"
    ); grep 看到
    def\|zzz
    因为 shell 删除了反斜杠,并且行为与第二种情况相同。
  4. 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
之间的唯一区别是必须转义的内容;功能是相同的。


1
投票

第一个失败,因为 grep 以编程方式转义管道,导致正则表达式中出现literal管道。

最后一次尝试失败,因为

\\\|
会导致 literal 反斜杠,然后在正则表达式中产生 literal 管道。

echo 'def|zzz'   | grep -e "def|zzz"    --> def|zzz
echo 'def\\|zzz' | grep -e "def\\\|zzz" --> def\|zzz

0
投票

根据 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
\\|
)在用双引号括起来时具有相同的正则表达式操作。

© www.soinside.com 2019 - 2024. All rights reserved.