使用sed和正则表达式从字符串中提取数字

32

我希望你能担任sed专家的角色。

我有一个表示路径名的字符串,其中会有两个数字。例如:

./pentaray_run2/Trace_220560.dat

我需要提取这些数字中的第二个数字 - 即220560。

我已经通过论坛的帮助,使用以下代码提取了所有数字(即2220560):

sed "s/[^0-9]//g"

或仅提取第一个数字:

sed -r 's|^([^.]+).*$|\1|; s|^[^0-9]*([0-9]+).*$|\1|'

但我需要的是第二个数字!!非常感谢任何帮助。

PS:我需要的数字始终是字符串中的第二个数字。

4个回答

31

这样可以吗?

sed -r 's/.*_([0-9]*)\..*/\1/g'

使用你的示例:

kent$   echo "./pentaray_run2/Trace_220560.dat"|sed -r 's/.*_([0-9]*)\..*/\1/g'
220560

太棒了,这个方法很有效。我猜下划线后面的空格是指只查找数字?在这种情况下,我总是可以期望有一个下划线,所以这会起作用。那么实际表达式中的哪一部分是这样的呢?是 .*_ 吗?Stackoverflow真是一个非常好的资源——我已经苦思冥想了几个小时了。顺便问一下,你觉得有没有办法在结尾处使用 \1——也许提取所有数字(连续的数字)作为子字符串并要求第二个数字。这对我和其他人将来可能会有用吗? - Steven

12

你可以用以下方法提取最后的数字:

sed -e 's/.*[^0-9]\([0-9]\+\)[^0-9]*$/\1/'

反向思考会更容易:

  1. 从字符串末尾开始匹配零个或多个非数字字符
  2. 匹配(并捕获)一个以上的数字字符
  3. 匹配至少一个非数字字符
  4. 匹配所有字符到字符串开头

匹配的第3个部分是“魔法发生”的地方,但它也限制了你的匹配结果必须在数字之前有至少一个非数字(例如无法匹配仅在字符串开头的单个数字,虽然可以通过在字符串开头插入非数字来解决这个问题)。

这种技巧是为了抵消 .* 的从左到右的贪婪性 (第4部分)。 没有第3部分,第4部分将尽可能地匹配所有内容,包括数字,但有了第3部分,匹配会确保它停止以允许第1和第2部分至少捕获一个非数字后跟一个数字,从而捕获数字。


9
如果需要使用 grep
$ echo './pentaray_run2/Trace_220560.dat' | grep -oP '\d+\D+\K\d+'
220560

使用相同的正则表达式,Perl 更加便携:

echo './pentaray_run2/Trace_220560.dat' | perl -lne 'print $& if /\d+\D+\K\d+/'
220560

我认为这种方法比使用sed更加简洁和强大。

6
这可能适用于您(GNU sed):
sed -r 's/([^0-9]*([0-9]*)){2}.*/\2/' file

这将提取第二个数字:

sed -r 's/([^0-9]*([0-9]*)){1}.*/\2/' file

这会提取第一个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接