【问题标题】:sed: print only matching groupsed:只打印匹配组
【发布时间】:2013-07-04 21:36:07
【问题描述】:

我想获取最后两个数字(一个整数,一个浮点数;后跟可选的空格)并只打印它们。

例子:

foo bar <foo> bla 1 2 3.4

应该打印:

2 3.4

到目前为止,我有以下内容:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

会给我

foo bar <foo> bla 1 replacement

但是,如果我尝试将其替换为第 1 组,则会打印整行。

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

如何仅打印与组中正则表达式匹配的行部分?

【问题讨论】:

  • 这个问题要求的不仅仅是打印。具有适当权限的人应修改此问题。
  • 为了避免误报 (int + ...) 和 (int + int) 使用:sed -nr 's/.*([0-9]+[\ \t][0-9]+.[0-9]+[\ \t]*$)/\1/p'

标签: regex sed


【解决方案1】:

匹配整行,所以在你的正则表达式的开头添加一个.*。这会导致整行替换为组的内容

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4

【讨论】:

  • 我必须添加 -r 或 `--regexp-extended` 选项,否则我会收到 invalid reference \1 on s' 命令的 RHS ` 错误。
  • @DanielSokolowski 我认为如果您使用 () 而不是 \(\),则会出现该错误。
  • 如果您要提取的字符串并不总是在行尾,请记住将.* 添加到正则表达式的末尾。
  • 这对我不起作用,因为 .* 是贪婪的,而 sed 没有非贪婪的 .*?
  • @DanielDarabos 只需提一下() 不会在 ubuntu 16.04 中引发错误。所以我认为这个评论已经过时了。
【解决方案2】:

grep 是正确的提取工具。

使用您的示例和您的正则表达式:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4

【讨论】:

  • 非常适合整个团队,尽管sed is needed for individual groups
  • grep -o 不移植到运行 msysgit 但 sed 的系统上。
  • 查看@jozxyqk 链接的问题以获得使用前瞻和后瞻来解决这个问题的答案。
  • 您可以通过管道grep -o 调用从模式中提取组。 stackoverflow.com/a/58314379/117471
  • 注意更复杂的正则表达式你需要使用`grep -Eo'
【解决方案3】:

对于另一种选择,我会选择 awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

这将在空格上拆分输入(我在这里使用 STDIN,但您的输入很容易成为文件),然后打印出最后一个字段,然后打印出最后一个字段。 $NF 变量保存在空间爆炸后找到的字段数。

这样做的好处是,最后两个字段之前的内容是否发生变化并不重要,只要您只想要最后两个字段,它就会继续工作。

【讨论】:

    【解决方案4】:

    cut 命令就是针对这种情况而设计的。它将在任何分隔符上“剪切”,然后您可以指定应该输出哪些块。

    例如: echo "foo bar &lt;foo&gt; bla 1 2 3.4" | cut -d " " -f 6-7

    将导致输出: 2 3.4

    -d 设置分隔符

    -f 选择要输出的“字段”范围,在这种情况下,它是原始字符串的第 6 到第 7 块。您也可以将范围指定为列表,例如6,7

    【讨论】:

    • 要仅打印某些列,请通过管道传送到 awk '{ print $2" "$6 }'
    • @nurettin 我认为您的评论可能是针对 awk 答案之一。
    • 我在访问此页面时尝试了 cut 并意识到它的局限性,并决定在 awk 中编写一个更通用的版本,而不是作为评论来提高这篇文章的质量。
    • 是的,我认为这属于涉及 awk 的不同答案。执行您所写的剪切命令是:cut -d " " -f 2,6
    • 啊,我不知道,我以为你只能给出范围。谢谢你。
    【解决方案5】:

    我同意@kent 的观点,即这非常适合grep -o。如果您需要在模式中提取组,可以使用第二个 grep 来完成。

    # To extract \1 from /xx([0-9]+)yy/
    $ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
    123
    4
    
    # To extract \1 from /a([0-9]+)b/
    $ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
    678
    9
    

    当我看到 2 个对 grep/sed/awk 的调用通过管道传输时,我通常会感到畏缩,但这并不总是错误的。虽然我们应该锻炼我们高效做事的技能,但“愚蠢的一致性是小脑袋的妖精”和“真正的艺术家船”。

    【讨论】:

    • 就像你添加了-E 标志,允许更复杂的正则表达式
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 2023-03-25
    • 2017-05-30
    相关资源
    最近更新 更多