为什么
grep e\\.g\\. <<< "this is an e.g. wow"
和
grep e\.g\. <<< "this is an e.g. wow"
做同样的事情?
如果我添加第三个斜杠,也会得到相同的结果。但是,一旦我添加第四个斜杠,它就不再起作用了。这与旧课程考试中的一个问题有关。它询问带有两个反斜杠的那个是否可以输出带有“eg”的行,我最初认为它不起作用,但我试图确保它确实起作用。解释是什么?
答案1
首先,请注意单斜杠匹配过多:
$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
e.g.
据,直到...为止重击就而言,转义期与句期相同。 Bash 将句点传递给grep。对于 grep,句点可以匹配任何内容。
现在,考虑:
$ echo $'eegg \n e.g.' | grep e\\.g\\.
e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$
当 Bash 看到双斜杠时,会将其简化为单斜杠并将其传递给 grep,在上面三个测试中的第一个测试中,如我们所愿,在句点之前看到一个单斜杠。因此,这是正确的做法。
通过三重斜线,Bash 将前两个斜线减少为单斜线。然后它看到了\.
。由于转义句点对 Bash 没有特殊意义,因此它被简化为普通句点。结果是 grep 正如我们所希望的那样,在句点之前看到了一个斜杠。
通过四个斜杠,Bash 将每对斜杠减少为单个斜杠。 Bash 将两个斜杠和一个句点传递给 grep。 grep 看到两个斜杠和一个句点,并将两个斜杠减少为单个斜杠文字削减。除非输入有一个斜杠后跟任何字符,否则没有匹配项。
为了最后说明这一点,请记住在单引号内,所有字符都是文字。因此,给定以下三个输入行,grep 命令仅匹配输入中带有文字斜杠的行:
$ echo 'eegg
e.g.
e\.g\.' | grep e\\\\.g\\\\.
e\.g\.
Bash 行为总结
对于 Bash,规则是
两个斜杠减少为一个斜杠。
普通字符前面的斜线(如句点)只是普通字符(句点)。
因此:
$ echo \. \\. \\\. \\\\.
. \. \. \\.
有一个简单的方法可以避免所有这些混乱:在 Bash 命令行上,正则表达式应该放在单引号中。在单引号内,Bash 保留所有内容。
$ echo '\. \\. \\\. \\\\.' # Note single-quotes
\. \\. \\\. \\\\.
答案2
仅对于您的字符串,输出是相同的,但通常这些正则表达式会执行不同的操作。让我们稍微修改一下您的示例,添加第二个模式e,g,
(带逗号)、第三个模式e\.g\.
(点)、第四个模式e\,g\,
(逗号)以及-o
grep 选项以仅打印匹配的部分。
在以下情况下
.
匹配任何字符(注意''
周围e.g.
,我稍后会谈到)$ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,' e.g. e,g,
接下来我们
.
用反斜杠转义\
,因此只有文字.
会被匹配:$ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,' e.g.
但我们可以
\
用另一个转义\
,以便文字\
将匹配后跟.
(即任何字符):$ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,' e\.g\. e\,g\,
但如果我们只想
\.
不匹配\,
,那么还\
需要另一个来转义点的特殊含义:$ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,' e\.g\.
现在,因为您没有使用''
around grep 参数,所以您需要添加另一个反斜杠以将反斜杠从 shell 解释中转义,因此:
grep 'e\.g\.' => grep e\\.g\\.
grep 'e\\.g\\.' => grep e\\\\.g\\\\. (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
答案3
当您执行 a 时grep e\.g\.
,shell 正在消耗反斜杠,因此您正在执行 a grep e.g.
,它匹配。当您执行 a 时grep e\\.g\\.
,shell 再次使用斜杠,现在您执行 a grep e\.\g.
,它再次匹配。现在,shell 的反斜杠看起来像\\
.因此,当您有 时\\
,第一个是转义序列,第二个是文字反斜杠。当您执行 a 时grep e\\\.g\\\.
,它最终仍然是,因为在第一个字符之前grep e\.\g.
没有转义序列 ( )使其成为文字。请记住 \ 是一个反斜杠,因此最终是,这显然不匹配。\
\
\
grep e\\\\.\\\\g
grep e\\.g\\.
要查看 shell 如何查看您正在执行的操作,请使用 echo (例如echo grep e\\.g\\. <<< "this is an e.g. wow"
vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow"
)
答案4
这两个命令仅针对您的输入产生相同的输出,但在其他方面它们是不同的。为了理解发生了什么,我们必须先知道参数是如何解释的,bash
然后是如何解释的grep
。
在 bash 中转义
\
是一个特殊字符,它取消了后续字符(包括\
其本身)的特殊含义。如果后面的字符没有特殊含义,则不加更改地传递。带有命令和结果的示例:
echo \a
:a
— 转义的普通字符给出了该字符echo \\
:\
— 特殊字符转义给出了该字符echo \\\a
:\a
——组合特殊、普通echo \\\\
:\\
— 组合特殊、特殊
echo
bash
将在解释后打印结果字符串。更多信息:bash 文档,bash 黑客维基,POSIX规范。
.
中没有特殊含义bash
。它是 shell 的一个普通字符。以下是与您的示例相关的序列:
echo .
:.
echo \.
:.
echo \\.
:\.
echo \\\.
:\.
echo \\\\.
:\\.
bash 中文字字符串的更简单解决方案
要按字面意思传递参数,bash
可以使用单引号'
转义。在单引号之间,您不必关心字符的特殊含义,因为单引号是唯一具有特殊含义的字符。您可以在将字符串的第一部分括起来后插入单引号。例子:
echo 'part1'\''part2'
: part1'part2
grep 中的正则表达式
\
是一个转义字符,其含义与 中类似bash
。.
是一个特殊字符代表任何字符的一次出现。看:POSIX 正则表达式,GNU grep 正则表达式。正则表达式示例:
.
— 匹配任何字符,例如a
or.
\.
— 仅.
字面匹配
你的例子
在下面每个示例的第二行,您将找到带有单引号的等效项,'
显示哪个文字字符串传递bash
给grep
。然后,在grep
执行转义之后,示例中唯一可能的特殊字符是.
匹配任何字符。第三行是表达式匹配内容的描述。
grep e.g. <<< "this is an e.g. wow"
grep 'e.g.' <<< "this is an e.g. wow"
e
任何字符g
任何字符—匹配e.g.
和可能的其他字符串喜欢eagb
grep e\.g\. <<< "this is an e.g. wow"
grep 'e.g.' <<< "this is an e.g. wow"
e
任何字符g
任何字符—匹配e.g.
和可能的其他字符串喜欢exgy
grep e\\.g\\. <<< "this is an e.g. wow"
grep 'e\.g\.' <<< "this is an e.g. wow"
e.g.
字面上地 -e.g.
仅匹配grep e\\\.g\\\. <<< "this is an e.g. wow"
grep 'e\.g\.' <<< "this is an e.g. wow"
e.g.
字面上地 -e.g.
仅匹配grep e\\\\.g\\\\. <<< "this is an e.g. wow"
grep 'e\\.g\\.' <<< "this is an e.g. wow"
e\
任何字符g\
任何字符—不匹配e.g.