如何在Bash中从字符串中获取最后一个数字?

15

抱歉,我会更好地解释一下。

如何从字符串中获取最后一个数字?

一些通用字符串的示例:

If str=str1s2
echo $str | cmd? 
I get 2

If str=234ef85
echo $str | cmd? 
I get 85

 If str=djfs1d2.3
echo $str | cmd? 
I get 3
"

"cmd?" 是我想要的命令/脚本

"

也许你可以计算字符串的长度,然后反向迭代匹配数字,当下一个匹配项为非数字时,你就得到了整个数字。http://www.thegeekstuff.com/2010/07/bash-string-manipulation/ - pokeybit
8个回答

19

你只需要 grep -Eo '[0-9]+$'

gv@debian:~$ echo 234ef85 |grep -Eo '[0-9]+$'          ## --> 85
gv@debian:~$ echo 234ef856 |grep -Eo '[0-9]+$'         ## --> 856
gv@debian:~$ echo 234ef85d6 |grep -Eo '[0-9]+$'        ## --> 6
gv@debian:~$ echo 234ef85d.6 |grep -Eo '[0-9]+$'       ## --> 6
gv@debian:~$ echo 234ef85d.6. |grep -Eo '[0-9]+$'      ## --> no result
gv@debian:~$ echo 234ef85d.6.1 |grep -Eo '[0-9]+$'     ## --> 1
gv@debian:~$ echo 234ef85d.6.1222 |grep -Eo '[0-9]+$'  ## --> 1222

2
请问,您能解释一下 grep -Eo 和 '[0-9]+$' 吗? - Phocs
2
-E:扩展grep,-o:仅获取匹配部分。在正则表达式部分,它查找位于以$标记的行末之前的一个或多个数字([0-9]+)。 - cya

9
您可以使用extglob进行参数扩展。首先,从末尾删除数字,然后再删除开头部分得到的内容。
#!/bin/bash
shopt -s extglob
for str in str1s2 djfs1d2.3 fefwfw4rfe45 234ef8 ; do
    without_number=${str%%+([0-9])}
    echo ${str#$without_number}
done

2
干得好。我学到了新东西。组合模式。启用extglob后,有5个额外的组合模式可用。(参见:在man bash的第1403行左右...我已经做了很长时间 :) - David C. Rankin
这突显了一个大部分未被探索的领域。谢谢。 - hmedia1

4

您可以使用 grep

rev <<< "$str" | grep -Eo "[0-9]*" | head -1 |rev

编辑: 当我使用tail -1时,不需要rev,但是当你像@Vasiliou一样仅添加行尾标记$时,head/tail过于复杂(我已经点赞了他的答案)。没有revheadgrep解决方案比sed更好。 我删除了我的备注“最好使用sed”。

sed -r 's/.*[^0-9]+([0-9]*)$/\1/' <<< "$str" 

你能解释一下 grep -Eo 吗? - Phocs
如果可能的话,看到使用 POSIX 扩展正则表达式真是太好了 - 许多人似乎只会直接使用 PCRE ++。 - hmedia1
1
选项 E 是用于扩展表达式的,o 是用于在不同行上显示所有匹配的子字符串。我编辑了我的答案,@GeorgeVasiliou 给出了更好的答案(您可以将所选答案切换到他的答案)。 - Walter A
@WalterA 谢谢! - Phocs
谢谢你,沃尔特。你是一个好人。 - George Vasiliou

3
从字符串中获取所有数字:
grep -Eo '[0-9]+'

从字符串中获取最后一个数字:
grep -Eo '[0-9]+' | tail -1

对George的回答进行一些扩展..

  • -E 表示扩展正则表达式
  • -o 表示将每个匹配部分单独打印到一行

3

使用awk:

输入:

 str1 = "str1s2"
 str2 = "djfs1d2.3"
 str3 = "fefwfw4rfe45"
 str4 = "234ef8"

命令:

tr = \  < INPUT  |
awk '{ match($2,"[0-9]*\"$"); 
       printf "%s: %s\n", $1, substr($2,RSTART,RLENGTH-1);  }'

输出:

str1: 2
str2: 3
str3: 45
str4: 8

你很棒,加入了一个 awk 变体。 - hmedia1

2

简短的 gawk 方法(适用于多个变量):

echo "$str1 $str2 $str3 $str4 " | awk -v FPAT="[0-9]+ " '{for(i=1;i<=NF;i++) print "str"i": "$i}'

输出结果:
str1: 2 
str2: 3 
str3: 45 
str4: 8 

  • FPAT="[0-9]+ " - 正则表达式匹配字段,而不是匹配字段分隔符

由于您更改了初始条件:
对于单个字符串,这将更加简单:

echo djfs1d2.3 | awk -v FPAT="[0-9]+" '{print $NF}'
3

不错的技巧。看起来可以直接使用 awk -v FPAT="[0-9]+ " '{print $NF}' 这种 FPAT 方法回答 OP 的问题。 - George Vasiliou
@GeorgeVasiliou,好的,是的。我没看到楼主改变了他最初的输入。 - RomanPerekhrest

2
刚刚发现了一种在纯Bash中以高效的方式完成这个任务的方法,没有使用管道或显式条件语句。
# ##            Erase all occurrences of pattern from beginning of string:
# *             Absolutely anything
# [![:digit:]]  Single non-digit character

num="${str##*[![:digit:]]}"

1
不需要生成子进程,因为在bash中有正则表达式匹配。例如,假设有字符串。
str=a34b56c

现在你可以做到:
if [[ $str =~ ([0-9]+)[^0-9]*$ ]]
then
  echo The last number is ${BASH_REMATCH[1]}
else
  echo The string "$str" does not contain digits
fi

这里的技巧是在模式中包含数字部分的括号。这样可以使用${BASH_REMATCH[1]}来获取该数字。

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