问题

问题

我回答了关于超级用户的这个问题这与 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),带有-PGNU 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

字符串可以通过多种方式进行匹配(从简单到更复杂):

  1. 作为静态字符串(假设 var='Hello World!'):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. 作为一个球体:

    echo ./* # 列表全部pwd 中的文件。
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    有基本的和扩展的 glob。该case示例使用基本的 glob。 bash[[示例使用扩展的 glob。第一个文件匹配可以是基本的,也可以是某些 shell 上的扩展,例如extglobbash 中的设置。在这种情况下两者是相同的。 Grep 无法使用 glob。

    a 中的星号全局与 a 中的星号含义不同正则表达式:

    * matches any number (including none) of任何字符
    * matches any number (including none) of the前面的元素

  3. 作为基本正则表达式 (BRE):

    echo "$var" | sed 's/W.*d//' # 打印:你好!
    grep -o 'W.*d' <<<"$var" # 打印世界!

    (基本)shell 或 awk 中没有 BRE。

  4. 扩展正则表达式(ERE):

    [[ "$var" =~ (H.*l) ]] # 比赛:你好,世界
    echo "$var" | sed -E 's/(d|o)//g' # print: 地狱咆哮!
    awk '/W.*d/{print $1}' <<<"$var" # 打印:你好
    grep -oE 'H.*l' <<<"$var" # 打印:你好,世界

  5. Perl 兼容的正则表达式:

    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]*
  • 哪个更好,在什么情况下?请举例说明。
    更好的?这取决于目标。没有更好的,每种都有不同的用途。我在上面提供了几个例子。您还需要更多吗?

相关内容