提取数字之前和下划线之后的字符串

提取数字之前和下划线之后的字符串

原来的字符串是这样的:

str-str001-002_01
str-str005-006_05

我想在数字之前和下划线之后提取字符串,所以它会是这样的:

str-str_01
str-str_05

我记得 sed 可以将模式分成这样的组:

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

但它打印:

str-str0002_01

然后我记得[0-9]只是一个数字,所以我用+号或*号尝试了它。然后它给出空结果。

ps:通过使用

echo 'str-str001-002_01' | sed -n 's/\(^.*\)\([0-9]-[0-9]\)\(.*$\)/\2/p'

我可以看到它匹配1-0

然后我尝试了:

echo 'str-str001-002_01' | sed -n 's/\(^.*\)\([0-9]\+-[0-9]\+\)\(.*$\)/\2/p'

它留下了前 2 个数字,并且只匹配

1-002

那么如何使其匹配001-002

答案1

这提供了所需的输出:

sed -nE 's/^([^0-9]*).*_([^_]+)$/\1_\2/p'

您的示例的输出

str-str_01
str-str_05

解释

  • sed -nE 's/…/…/p'- 使用 ERE,除非它们匹配,否则不要打印行
  • ^- 锚定到行的开头
  • ([^0-9]*)- 匹配尽可能长的模式,即至少一个非数字字符
  • .*_- 尽可能匹配(包括什么都不匹配),后面跟“ _
  • ([^_]+)- 匹配尽可能长的不是下划线的模式(至少一个字符)
  • $- 锚定到行尾
  • \1_\2- 将整行替换为第一个(…)匹配项“ _”和第二个(…)匹配项

您的尝试未按预期工作的原因是因为*(and +) 是贪婪的 - 它将消耗尽可能多的与前面的原子匹配的字符。因此,对于(.*)([0-9]+)应用于类似的ERE abc123.* 将消耗abc12,留下[0-9]+匹配3。你需要一个“不是数字" 限制第一个匹配:([^0-9]*)([0-9]+)得到abc123

答案2

$ cat file
str-str001-002_01
str-str005-006_05
$ sed 's/[0-9]\{3\}-[0-9]\{3\}//' file
str-str_01
str-str_05

这里的替换命令是匹配并删除NNN-NNN其中NNN的三位数。

匹配最后一个数字,用于1,代替3

$ sed 's/[0-9]\{1,\}-[0-9]\{1,\}//' file
str-str_01
str-str_05

这对应于+在扩展正则表达式中的使用。默认情况下使用的正则表达式sed是“基本”正则表达式,并且+会匹配文字加字符。大多数sed实现还支持扩展表达式-E

$ sed -E 's/[0-9]+-[0-9]+//' file
str-str_01
str-str_05

使用*,如[0-9]*-[0-9]*,将不起作用,因为它会匹配破折号str-str(其周围有零位数字)。


如果您觉得确实必须匹配整条线并捕获您想要保留的位,那么您也可以这样做。以下命令捕获初始非数字和最后一位,包括下划线:

$ sed 's/\([^0-9]*\).*\(_.*\)/\1\2/' file
str-str_01
str-str_05

然而,恕我直言,这有点难以破译,并对您在问题中从未提到的字符串的开头和结尾做出假设。例如,开头不能包含要删除的数字之前的数字,并且字符串的结尾将在最后的下划线,如果字符串的该部分中有多个下划线,则不必在要删除的数字之后。

您始终可以进一步添加此表达式以确保仅NNN-NNN不捕获该位,但这会使该表达式更难以理解。

相关内容