如何从字符串中提取数字?

34

我有一个包含路径的字符串。

string="toto.titi.12.tata.2.abc.def"
我想从这个字符串中提取数字。要提取第一个数字:
tmp="${string#toto.titi.*.}"
num1="${tmp%.tata*}"
提取第二个数字:
tmp="${string#toto.titi.*.tata.*.}"
num2="${tmp%.abc.def}"

所以要提取参数,我需要分两步来完成。如何用一步提取数字?


1
这个问题已经存在一段时间了。如果没有答案提供您所需的内容,那么您可以更新您的问题,以更清楚地阐明您的要求。 - ghoti
3
我认为echo ${string} | grep -o -E "[0-9]+"是最简洁且易于理解的方法(大多数人都知道grep)。来源:https://dev59.com/hGMm5IYBdhLWcg3wFL7s#52947167 - Trevor Boyd Smith
12个回答

31
您可以使用tr删除所有非数字字符,如下所示:
echo toto.titi.12.tata.2.abc.def | tr -d -c 0-9

3
这个输出似乎将所有数字混在一起,以你的例子为例,会得到 122。有什么方法可以将它们分开吗? - ghoti
为了将其设置为变量,请使用- PARAM = `echo toto.titi.12.tata.2.abc.def | tr -d -c 0-9` - Adir D

15

将所有单个数字提取出来,并逐行打印一个数字单词,可以通过管道符 - 进行传输。

tr '\n' ' ' | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' | tr -s ' ' | sed 's/ /\n/g'

步骤:

  • 将所有换行符替换为空格:tr '\n' ' '
  • 将所有非数字字符替换为空格:sed -e 's/[^0-9]/ /g'
  • 去除前导空格:-e 's/^ *//g'
  • 去除尾随空格:-e 's/ *$//g'
  • 将连续的空格缩减为一个空格:tr -s ' '
  • 将剩余的空格替换为换行符:sed 's/ /\n/g'

示例:

echo -e " this 20 is 2sen\nten324ce 2 sort of" | tr '\n' ' ' | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' | tr -s ' ' | sed 's/ /\n/g'

将会打印输出

20
2
324
2

13

这是一个简短的内容:

string="toto.titi.12.tata.2.abc.def"
id=$(echo "$string" | grep -o -E '[0-9]+')

echo $id // => output: 12 2

带有数字之间的空格。 希望能帮到您...


9
参数扩展似乎是当今的主流。
$ string="toto.titi.12.tata.2.abc.def"
$ read num1 num2 <<<${string//[^0-9]/ }
$ echo "$num1 / $num2"
12 / 2

当然,这取决于$string的格式。但至少对于您提供的示例,它似乎有效。

与anubhava的awk解决方案相比,这可能更为优越,因为后者需要一个子shell。我也喜欢chepner的解决方案,但是正则表达式比参数展开“重”(虽然显然要更精确)。 (请注意,在上面的表达式中,[^ 0-9] 可能看起来像是正则表达式原子,但实际上不是。)

您可以在bash手册中了解有关此形式或参数展开的信息。请注意, $ {string // this / that} (以及<<< )是一种bashism,不兼容传统的Bourne或posix shell。


2
你说的“它取决于$string的格式”具体是什么意思?我想不出任何会破坏它的例子。 - PesaThe
1
嘿,这是一个老问题。 :) 目前我能想到的唯一一件事是,如果有额外的数字,比如 aa12aa34aa56,而你只读取两个变量,那么尾随的数字会被添加到最后一个变量中,用空格分隔。如果这是一个问题,那么更好的解决方案可能是将字符串读入数组中:read -a nums <<<"${string//[^0-9]/ }" - ghoti

4
将您的字符串转换为如下数组:
$ str="toto.titi.12.tata.2.abc.def"
$ arr=( ${str//[!0-9]/ } )
$ echo "${arr[@]}"
12 2

3
如果您能提供您想要得到的输出结果,那么回答会更容易。如果您的意思是只想从字符串中获取数字并删除其他内容,您可以这样做:
d@AirBox:~$ string="toto.titi.12.tata.2.abc.def"
d@AirBox:~$ echo "${string//[a-z,.]/}"
122

如果您能稍微澄清一下,我可能能够提供更多帮助。

我更新了我的问题。我想提取12,然后再提取2,而不是同时提取这两个数字。 - MOHAMED

2
您也可以使用sed命令:
echo "toto.titi.12.tata.2.abc.def" | sed 's/[0-9]*//g'

在这里,sed 用空白替换了

  • 任何数字(类 [0-9]
  • 重复任意次数(*
  • 什么都不替换(第二个和第三个 / 之间没有任何东西),
  • g 表示全局。

输出将会是:

toto.titi..tata..abc.def

7
我认为OP想要输出数字而不是字符串。 - cchamberlain
2
如果你想要数字,使用 ^ 来反转匹配:echo "toto.titi.12.tata.2.abc.def" | sed 's/[^0-9]*//g' - Dario Seidl

1

你好,这是另一种使用“cut”命令的方法:

echo $string | cut -d'.' -f3,5 | tr '.' ' '

这将会给你以下输出:

12 2


1
使用正则表达式匹配:
string="toto.titi.12.tata.2.abc.def"
[[ $string =~ toto\.titi\.([0-9]+)\.tata\.([0-9]+)\. ]]
# BASH_REMATCH[0] would be "toto.titi.12.tata.2.", the entire match
# Successive elements of the array correspond to the parenthesized
# subexpressions, in left-to-right order. (If there are nested parentheses,
# they are numbered in depth-first order.)
first_number=${BASH_REMATCH[1]}
second_number=${BASH_REMATCH[2]}

1
使用 awk:
arr=( $(echo $string | awk -F "." '{print $3, $5}') )
num1=${arr[0]}
num2=${arr[1]}

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