如何在外壳中保留/删除变量中的数字?



我有一个变量,例如:

disk=/dev/sda1

我想提取:

  • 只有非数字部分(即/dev/sda)
  • 仅数字部分(即 1)

我将在需要磁盘和分区号的脚本中使用它。 如何在 shell 中做到这一点(主要是 bash 和 zsh)?

我正在考虑使用 Shell 参数扩展,但在文档中找不到工作模式。

基本上,我尝试了:

echo ${disk##[:alpha:]}

echo ${disk##[:digit:]}

但没有一个奏效。两人都回来了/dev/sda1

使用 bash 和 zsh 以及参数扩展:

disk="/dev/sda12"
echo "${disk//[0-9]/} ${disk//[^0-9]/}"

输出:

/
dev/sda 12

扩展的工作方式正好相反。使用[:digit:]您将仅匹配一位数字。您需要将所有内容匹配到或从数字开始,因此您需要使用*.

以下看起来不错:

$ echo ${disk%%[0-9]*} ${disk##*[^0-9]}
/dev/sda 1

要使用[:digit:]您需要双大括号,因为字符类是[:class:]的,并且它本身必须在[]内。这就是为什么我更喜欢0-9,少打字*。以下内容与上述相同:

echo ${disk%%[[:digit:]]*} ${disk##*[^[:digit:]]}

* - 理论上它们可能不相等,因为[0-9]可能会受到当前语言环境的影响,因此它可能不等于[0123456789],而是不同的东西。

在参数替换中使用模式时必须小心。这些模式不是正则表达式,而是路径名扩展模式或 glob 模式。

这个想法是删除最后一个数字,所以你想使用删除匹配的后缀模式(${parameter%%word})。在这里,我们删除word描述的匹配模式的最长实例。使用模式[0-9]很容易表示个位数,但是多位数数字更难。为此,您需要使用扩展的 glob 表达式:

*(pattern-list):匹配给定模式的零次或多次出现

因此,如果要删除最后一个数字,请使用:

$ shopt -s extglob
$ disk="/dev/sda1"
$ echo "${disk#${disk%%*([0-9])}} "${disk%%*([0-9])}"
1 dev/sda
$ disk="/dev/dsk/c0t2d0s0"
$ echo "${disk#${disk%%*([0-9])}} "${disk%%*([0-9])}"
0 /dev/dsk/c0t2d0s

我们必须使用${disk#${disk%%*([0-9])}}来删除前缀。它基本上搜索最后一个数字,删除它,使用余数并再次删除该部分。

您还可以使用模式替换(${parameter/pattern/string}),将模式锚定%#将模式锚定到参数的开头或结尾。(有关详细信息,请参阅man bash)。这完全等同于前面的解决方案:

$ shopt -s extglob
$ disk="/dev/sda1"
$ echo "${disk/${disk/%*([0-9])}/}" "${disk/%*([0-9])}"
1 dev/sda
$ disk="/dev/dsk/c0t2d0s0"
$ echo "${disk/${disk/%*([0-9])}/}" "${disk/%*([0-9])}"
0 /dev/dsk/c0t2d0s

最新更新