【问题标题】:Grep - how to output only the content of a capturing groupGrep - 如何仅输出捕获组的内容
【发布时间】:2020-02-11 05:13:52
【问题描述】:

我正在尝试为 grep 找到一种仅输出捕获组内容的方法。例如,如果我有以下文件:

hello1, please match me
hello2, please do not match me

我愿意

grep -Eo '(hello[0-9]+), please match me' file

输出hello1。但是它输出hello1, please match me

现在,我知道 grep -Po 'hello[0-9]+(?=, please match me)' 可以解决问题,但我认为必须有一种方法可以简单地返回捕获组,但我找不到任何信息(在网上和 man grep )。

是否有可能,或者捕获组只是为了进行反向引用?如果没有办法做到这一点,我会觉得很奇怪。

感谢您抽出宝贵时间,并随时批评这篇文章的构建方式!

【问题讨论】:

  • 据我所知,GNU grep 不支持仅获取捕获的组,除非您使用带有 PCRE 选项的环视...ripgrep(替代实现)确实支持您的身份问,但在精神上更像是sed提供的搜索和替换功能......所以,如果你需要操纵捕获组,sed会是更好的选择
  • 带有-P 的非消费(?=) 组允许在正则表达式中使用一种AND 函数。使用 grep 与您的正则表达式的另一种方法是将 grep 传递给 grep。那么在这里用管道 grep 到 grep 有什么问题呢?

标签: bash shell grep


【解决方案1】:

这个问题被问到ten years ago,所以我不会将其标记为重复。我还注意到没有给出 sed 解决方案,因为 OP 没有给出答案:

sed -nr 's/(hello[0-9]+), please match me/\1/p' test.txt
  • -n 代表安静(除非明确要求,否则不会打印任何内容)
  • -r 允许使用扩展正则表达式(此处避免在括号前使用\
  • s/reg/repl/p命令的意思是“如果正则表达式reg匹配当前行,用repl捕获的文本替换它,并打印它(/p)”

【讨论】:

  • 所以是的,在 grep 中似乎不可能做到这一点?您的解决方案似乎是更好的解决方案。我会等着看是否有人证明我们错了,但如果没有,我会把你的评论作为解决方案。
  • 现在循环了,因为g/re/p 的意思是“全局搜索正则表达式并打印”
  • @Amessihel 确实如此!我对哪一个接受有些怀疑,因为 rici 的回答也很好
  • @Amessihel,我会选择 Rici 的答案,因为它最接近我的要求。不过你的回答也很好,谢谢。
【解决方案2】:

如果您有pcregreppcre2grep,您可以使用-o1 命令行标志请求仅输出捕获组1。 (如果正则表达式中有更多捕获,则将 1 更改为其他数字。)

如果要输出多个捕获组,可以多次使用-o<i>N</i> 命令。

据我所知,grep -P 没有实现这个扩展。您将在 Debian/Ubuntu 软件包 pcre2-utils 中找到 pcre2greppcregrep 在包中 pcregrep

【讨论】:

  • 为什么 apt 在 Ubuntu 19.04 上找不到 PCRE2 为 pcre2grep$ sudo apt-get install pcre2grep; Reading package lists... Done; Building dependency tree; Reading state information... Done; E: Unable to locate package pcre2grep
  • @vstepaniuk:正如我上面所说,“你会在 Debian/Ubuntu 软件包 pcre2-utils 中找到 pcre2grep。”
  • 为什么在 Ubuntu 19.04 上找不到 PCRE2 作为 pcre2grep?
  • @vstepaniuk:因为使用 libpcre2 构建包含各种实用程序的包的人选择不构建仅包含 pcre2grep 的单独包。我不知道那个决定是怎么回事。你得问问他们。但是apt 只知道包名。
【解决方案3】:

可以使用ripgrep,一般看起来比grep好,像这样

rg '(hello[0-9]+), please match me' -or '$1' <file>

其中 ripgrep 使用-o--only matching-r--replace 仅输出带有$1 的第一个捕获组(引用以避免被shell 解释为变量)。

【讨论】:

    【解决方案4】:

    grepsedawk 具有不支持任何现代正则表达式功能的古老正则表达式引擎。我真的认为它们不再适合目的了。

    Perl 仍然适用的一件事是作为几乎所有单行代码中的替代品,因为它有一个非常好的现代正则表达式引擎和几个方便的命令行开关,-ne-pe.

    这些开关使 Perl 自动将您的表达式应用到输入的每一行,并无条件地打印结果,或者让您控制结果的打印。

    例如,要打印第一个 hello 后跟一个数字 (hello\d) 的所有行,hello\d 后跟 please match me,您可以这样做:

    perl -ne 'm/(hello\d) please match me/ && print "$1\n"' <file>
    

    有很多不错的网站列出了您可以使用 Perl 单行代码执行的常见任务,例如 this one

    我也认为ripgrep应该在每个人的工具箱中。

    【讨论】:

      【解决方案5】:

      只是一个awk 版本。

      awk -F, '/hello[0-9]+, please match me/ {print $1}' file
      hello1
      

      【讨论】:

        【解决方案6】:

        Perl 模式有一个棘手的方法

        $ echo "hello1, please match me" | rev | grep -oP 'em hctam esaelp ,\K[0-9]olleh' | rev
        hello1
        

        基本上使用\K 通过反转输入和搜索词来回溯。

        您也可以将反向搜索词外包给rev

        $ echo hello1, please match me | 
          rev | 
          grep -oP "$(echo hello1K\\, please match me | rev)" | 
          rev
        

        【讨论】:

        • 自从 OP 询问返回捕获组的简单方法后,此解决方案有什么好处?
        • @Amessihel 这确实不符合我的要求,当然,但我确实发现这是一个非常有趣的解决方案
        • 向后看比向前看更容易,这很可能就是为什么有\K 但没有一个用于向前看的原因。不是真正用于实际用途...
        猜你喜欢
        • 1970-01-01
        • 2011-02-16
        • 1970-01-01
        • 2012-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-20
        • 2020-08-05
        相关资源
        最近更新 更多