如何在 shell 中的变量中 keep/remove 数字?

How to keep/remove numbers in a variable in shell?

我有一个变量,例如:

disk=/dev/sda1

我要提取:

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

我正在考虑在文档中使用 Shell parameters expansions, but couldn't find working patterns

基本上,我试过:

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

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

但是 none 起作用了。两者都返回 /dev/sda1

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

以下看起来不错:

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

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

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

* - 理论上它们可能不相等,因为 [0-9] 会受到当前语言环境的影响,所以它可能不等于 [0123456789],但会有所不同。

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

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

输出:

/dev/sda 12

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

想法是删除最后一个数字,因此您想利用删除匹配的后缀模式 (${parameter%%word})。这里我们删除了 word 描述的匹配模式的最长实例。使用模式 [0-9] 可以轻松表示单个数字,但是 multi-digit 数字更难。为此,您需要使用扩展的 glob 表达式:

*(pattern-list): Matches zero or more occurrences of the given patterns

所以如果你想删除最后一个数字,你使用:

$ 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