我回答了关于超级用户的这个问题这与 grep 输出时使用的正则表达式有关。
我给出的答案是这样的:
tail -f log | grep "some_string.*some_string"
然后,在我的回答的三个评论中@鲍勃写了这个:
.*
是贪婪的,可能会捕获比您想要的更多的东西。.*?
通常会更好。
那么这个,
the
?
是 的修饰符*
,使其变得惰性而不是默认的贪婪。假设PCRE。
我用谷歌搜索PCRE
,但无法理解这在我的答案中有何意义?
最后这个,
我还应该指出,这是正则表达式(grep 默认执行 POSIX 正则表达式),而不是 shell glob。
我只知道什么是正则表达式以及它在 grep 命令中的基本用法。所以,我无法收到这 3 条评论中的任何一条,我心中有以下问题:
- 使用上有什么区别
.*?
与.*
? - 哪个更好,在什么情况下?请举例说明。
如果有人可以的话,理解这些评论也会很有帮助
更新:作为问题的答案正则表达式与 Shell Glob 有何不同? @Kusalananda假如这个链接在他的评论中。
注:如有需要,请阅读我对这个问题的回答在回答之前参考上下文。
答案1
假设我采用如下字符串:
can cats eat plants?
使用贪婪运算符c.*s
将匹配整个字符串,因为它以 开头c
并以 结尾s
,作为贪婪运算符,它会继续匹配直到最后出现 s。
而使用惰性c.*?s
只会匹配直到s
找到第一次出现,即 string can cats
。
从上面的例子中,您可能可以得出以下结论:
“贪心”意味着匹配尽可能长的字符串。 “懒惰”意味着匹配尽可能短的字符串。将 a 添加?
到量词(如*
、+
、?
或 ){n,m}
会使其变得惰性。
答案2
阿肖克已经指出.*
和之间的区别.*?
,所以我只提供一些额外的信息。
grep
(假设是GNU版本)支持4种字符串匹配方式:
- 固定字符串,带有
-F
选项 - 基本正则表达式 (BRE),默认
- 扩展正则表达式 (ERE),带有
-E
选项 - Perl 兼容的正则表达式 (PCRE),带有
-P
GNU grep 中的选项
grep
默认使用 BRE。
BRE 和 ERE 记录在常用表达POSIX 和 PCRE 的章节记录在官方网站。请注意,功能和语法可能因实现而异。
值得一提的是,BRE和ERE都不支持懒惰:
多个相邻重复符号(“+”、“*”、“?”和间隔)的行为会产生未定义的结果。
因此,如果您想使用该功能,则需要使用 PCRE:
# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants
# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats
你能解释一下吗
.*
与.*?
?
.*
用于匹配可能的“最长” 1模式。.*?
用于匹配可能的“最短” 1模式。
根据我的经验,最想要的行为通常是第二个。
例如,假设我们有以下字符串,我们只想匹配 html 标签2,而不是它们之间的内容:
<title>My webpage title</title>
现在.*
比较.*?
:
# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>
# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>
1. 正则表达式上下文中“最长”和“最短”的含义有点棘手,正如库萨罗南达指出的。请参阅官方文档以获取更多信息。
2.不建议使用正则表达式解析 html。这只是用于教育目的的示例,请勿在生产中使用它。
答案3
字符串可以通过多种方式进行匹配(从简单到更复杂):
作为静态字符串(假设 var='Hello World!'):
壳
[ "$var" = "Hello World!" ] && echo yes
grepecho "$var" | grep -F "Hello"
巴什grep -F "Hello" <<<"$var"
作为一个球体:
壳
echo ./*
# 列表全部pwd 中的文件。
壳case $var in (*Worl*) echo yes;; (*) echo no;; esac
巴什[[ "$var" == *"Worl"* ]] && echo yes
有基本的和扩展的 glob。该
case
示例使用基本的 glob。 bash[[
示例使用扩展的 glob。第一个文件匹配可以是基本的,也可以是某些 shell 上的扩展,例如extglob
bash 中的设置。在这种情况下两者是相同的。 Grep 无法使用 glob。全局
* matches any number (including none) of
任何字符。
正则表达式* matches any number (including none) of the
前面的元素。作为基本正则表达式 (BRE):
sed
echo "$var" | sed 's/W.*d//'
# 打印:你好!
grepgrep -o 'W.*d' <<<"$var"
# 打印世界!
(基本)shell 或 awk 中没有 BRE。
扩展正则表达式(ERE):
巴什
[[ "$var" =~ (H.*l) ]]
# 比赛:你好,世界
sedecho "$var" | sed -E 's/(d|o)//g'
# print: 地狱咆哮!
awkawk '/W.*d/{print $1}' <<<"$var"
# 打印:你好
grepgrep -oE 'H.*l' <<<"$var"
# 打印:你好,世界
Perl 兼容的正则表达式:
grep
grep -oP 'H.*?l
# 打印:赫尔
仅在 PCRE 中 a*?
具有某些特定的语法含义。
它使星号变得懒惰(不贪婪):用懒惰代替贪婪。
$ grep -oP 'e.*l' <<<"$var"
ello Worl
$ grep -oP 'e.*?l' <<<"$var"
el
这只是冰山一角,还有贪婪、懒惰, 和温顺或占有欲。还有前瞻和后瞻但这些不适用于星号*
。
有一种替代方法可以获得与非贪婪正则表达式相同的效果:
$ grep -o 'e[^o]*o' <<<"$var"
ello
这个想法非常简单:不要使用点.
,否定下一个要匹配的字符[^o]
。使用网络标签:
$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>
以上应该完全澄清了所有 @Bob 3 的评论。释义:
- .* 是常见的正则表达式,而不是 glob。
- 只有正则表达式可以与 PCRE 兼容。
- 在 PCRE 中:a ?修改*量词。
.*
是贪心.*?
不是。
问题
使用上有什么区别。?与.?
- A
.*?
仅在 PCRE 语法中有效。 - A
.*
更便携。 - 通过用否定的字符范围替换点可以达到与非贪婪匹配相同的效果:
[^a]*
- A
哪个更好,在什么情况下?请举例说明。
更好的?这取决于目标。没有更好的,每种都有不同的用途。我在上面提供了几个例子。您还需要更多吗?