使用 `sed` 查找重复模式

find recurring pattern with `sed`

我正在使用 GNU bash 4.3.48

我预计

echo "23S62M1I19M2D" | sed 's/.*\([0-9]*M\).*//g'

会输出 62M19M... 但它不会。

sed 's/\([0-9]*M\)//g' 删除所有 [0-9]*M 并检索 23S1I2D。但是小组 </code> 没有像我想象的那样工作。</p> <p><code>sed 's/.*\([0-9]*M\).*/ /g', 检索 M...

我做错了什么?

谢谢!

您的替换可能有效,但不是您认为的那样。

在替换 s/\(foo...\)// 中,</code> 匹配任何 <code>\(...\) 匹配和捕获的内容,因此您的替换将 foo... 替换为 foo...

% echo "1234ABC" | sed 's/\([A-Z]\)/--/'g
1234-A--B--C-

因此您需要匹配 更多,但只捕获匹配的一部分。例如:

echo "23S62M1I19M2D" | sed 's/[0-9]*[A-LN-Z]*\([0-9]*M\)//g'
62M19M2D

sed 's/.*\([0-9]*M\).*//g' 的情况下(出现在对问题的编辑中,还是我只是错过了它?),.* 匹配“贪婪”——它匹配的次数与它可能可以,因此包括 M 之前的数字。在上面的例子中,要求 [A-LN-Z] 位于未捕获部分的末尾,因此数字必须与捕获内的 [0-9] 匹配。

在编写或解释正则表达式时,清楚地了解“贪婪”的含义是一个非常重要的想法。

如果您知道您只会遇到后缀 SMID,另一种方法是明确删除您不希望遇到的组合我想要:

echo "23S62M1I19M2D" | sed 's/[0-9]\+[SID]//g'

这给出了预期的:

62M19M

更新: 此变体产生相同的输出,但拒绝所有非数字、非 M 后缀:

echo "23S62M1I19M2D" | sed 's/[0-9]\+[^0-9M]//g'    

问题是 .* 是贪心的。由于只有 M 是必须的,当引擎找到最后一个 M 时,它满足正则表达式,因此匹配所有字符串, M 被捕获并因此在替换为 </code> 后保留反向引用。</p> <p>这意味着,您无法使用 <code>sed 轻松做到这一点。你可以用 Perl 更容易地做到这一点,因为它支持匹配和跳过模式:

#!/bin/bash
perl -pe 's/\d+M(*SKIP)(*F)|.//g' <<< "23S62M1I19M2D"

online demo。模式匹配

  • \d+M(*SKIP)(*F) - 一个或多个数字,M,然后省略匹配,从失败位置开始搜索下一个匹配
  • |. - 或匹配换行符以外的任何字符。

或者简单地匹配所有匹配项并将它们连接起来:

perl -lane 'BEGIN{$a="";} while (/\d+M/g) {$a .= $&} END{print $a;}' <<< "23S62M1I19M2D"

所有 \d+M 匹配都附加到 $a 变量,该变量在处理字符串结束时打印。

使用您展示的示例和 awk 您可以尝试以下程序。

echo "23S62M1I19M2D" | 
awk '
{
  val=""
  while(match([=10=],/[0-9]+M/)){
    val=val substr([=10=],RSTART,RLENGTH)
    [=10=]=substr([=10=],RSTART+RLENGTH)
  }
  print val
}
'

解释: 简单的解释是,使用 echo 打印值并将其​​作为标准输入发送到 awk 程序。在 awk 程序中使用其 match 函数匹配其中提到的正则表达式 (/[0-9]+M) 运行 循环查找每一行中的所有匹配项并在最后打印收集的匹配值每行。

这可能对你有用 (GNU sed):

sed -nE '/[0-9]*M/{s//\n&\n/g;s/(^|\n)[^\n]*\n?//gp}' file

用换行符包围匹配项,然后删除不匹配的部分。

替代方案,使用 grep 和 tr:

grep -o '[0-9]*M' file | tr -d '\n' 

N.B。 tr 删除所有换行符(包括最后一个)恢复最后一个换行符,使用:

grep -o '[0-9]*M' file | tr -d '\n' | paste  

备用解决方案会将所有结果连接成一行。要使用第一个解决方案获得相同的结果,请使用:

sed -nE '/[0-9]*M/{s//\n&\n/g;s/(^|\n)[^\n]*\n?//g;H};${x;s/\n//gp}' file