![Bash:在搜索和替换中替换给定模式的最后一次出现](https://linux22.com/image/187760/Bash%EF%BC%9A%E5%9C%A8%E6%90%9C%E7%B4%A2%E5%92%8C%E6%9B%BF%E6%8D%A2%E4%B8%AD%E6%9B%BF%E6%8D%A2%E7%BB%99%E5%AE%9A%E6%A8%A1%E5%BC%8F%E7%9A%84%E6%9C%80%E5%90%8E%E4%B8%80%E6%AC%A1%E5%87%BA%E7%8E%B0.png)
${PARAMETER/PATTERN/STRING}
${PARAMETER//PATTERN/STRING}
This one can substitute (replace) a substring matched by a
pattern, on expansion time. The matched substring will be entirely
removed and the given string will be inserted.
The first one (one slash) is to only substitute the first
occurrence of the given pattern, the second one (two slashes) is
to substitute all occurrences of the pattern.
这种方法也适用于 Bash 数组,使用array[@]
语法。但是假设有人想要替换给定模式的最后一次出现。 Bash 显然没有提供执行此操作的方法。
对于单个字符串和一个只是普通字符串的模式,可以这样做(参见例如https://unix.stackexchange.com/a/187920/4671)
s=/foo/bar/baz
echo ${s%/*}/prefix-${s##*/}
这使
/foo/bar/prefix-baz
但这对于数组不起作用。但是,可以在数组上编写一个显式循环,如下所示
a=(foo/bar/baz foo/bar/boo)
b=()
for i in "${a[@]}"; do
b+=("${i%/*}/prefix-${i##*/}")
done
echo "${b[@]}"
这使
foo/bar/prefix-baz foo/bar/prefix-boo
所以,我有两个相关的问题。
为什么 Bash 不提供替换最后一场比赛的选项?这仅仅是一个未实现的功能,还是存在无法实现的技术原因?因为这似乎是一个奇怪的空白。欢迎与其他 shell 进行比较。
有没有办法在不循环数组的情况下替换最后一个匹配项?显然 Zsh 是行家所使用的,所以如果您愿意,请随意提供 Zsh 解决方案。
答案1
我确信它们存在,但我不知道任何语言中的任何运算符具有该功能。如果您查看sed
s s/../../
,则可以将第 n 个出现替换为s/../../n
,但不能从最后一个出现的次数开始。
我想这会让它成为一个过于模糊的功能。
在这里,您可以使用ksh
's${var/pattern/replacement}
和反向引用。
和zsh
:
set -o extendedglob
b=("${a[@]/%(#m)[^\/]#/prefix-$MATCH}")
(其中(#m)
激活对 中匹配部分的捕获$MATCH
)
或者:
b=("${(S)a[@]/%(#b)\/(*)//prefix-$match[1]}")
(其中(#b)
激活反向引用,将组匹配的部分捕获(...)
到数组的元素中$match
,并使S
匹配不那么贪婪)。
和ksh93
:
b=("${a[@]/%*([^\/])/prefix-\1}")
在 中zsh
,您还可以执行以下操作:
dirs=($a:h)
new_bases=(prefix-$^a:t)
b=()
printf -v b %s/%s ${dirs:^new_bases}