我有一个变量,例如:
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