我已经阅读了GNU 的“Shell Parameter Expansion”文档,给出了以下语法:
${parameter//pattern/string}
该模式被扩展以产生一个模式,就像在文件名扩展中一样。参数被扩展,模式 与其值的最长匹配被替换为字符串......如果有两个斜杠分隔参数和模式......,所有匹配的模式都被替换为字符串。
鉴于上面的短语“模式的所有匹配项都替换为字符串”,我希望在一个操作中一次将模式的多个实例全部替换为字符串 ,类似于标志如何与全局搜索和替换一起使用正则表达式中的命令。/g
我有一段开源代码(一个名为 的函数remove_from_path
),其实现如下:
remove_from_path() {
local path_to_remove="$1"
local path_before
local result=":${PATH//\~/$HOME}:"
local counter=0
while [ "$path_before" != "$result" ]; do
counter+=1
echo "counter: $counter"
path_before="$result"
result="${result//:$path_to_remove:/:}"
done
result="${result%:}"
echo "${result#:}"
}
原始代码不包含该变量——我添加了该变量以检查循环将执行counter
多少次迭代。while
正如我们所看到的,该行result="${result//:$path_to_remove:/:}"
使用了 GNU 文档中提到的相同的双正斜杠语法。鉴于此,我希望while
循环只会执行一次,因为path_to_remove
应该result
一次性删除所有实例。
然而,情况似乎并非如此。在bash
shell ( version 3.2.57
) 中,我将 my 更新$PATH
为以下内容:
bash-3.2$ PATH="/foo/bar/baz:/foo/bar/baz:/foo/bar/baz:buzz"
然后我将上述函数复制/粘贴到我的 shell 中,然后运行它。我看到以下内容:
bash-3.2$ remove_from_path "/foo/bar/baz"
counter: 01
counter: 011
counter: 0111
buzz
请忽略计数器的递增没有按我预期的方式工作的事实,因为我们仍然可以看到while
循环的 3 次执行。如果${parameter//pattern/string}
双正斜杠语法确实用字符串替换了所有匹配的模式,为什么没有在循环的一次迭代中完成呢?为什么我们需要 3 次迭代?while
我想我已经找到了问题,但如果我错了,请让我现在。
在
$result
变量中你有这个字符串:当您申请时,您
result="${result//:$path_to_remove:/:}"
将所有出现的 替换:/foo/bar/baz:
为:
。但是鉴于该模式,第二条路径并不真正匹配,因为:
. 例如:粗体路径是您的模式的出现。
您可以尝试使用以下方法进行测试:
正如您在上面看到的,第二条路径 (
/for/bar/baz2
) 不受您使用的模式的影响。所以你可以用参数扩展做的是这样的:
所以你的
remove_from_path
函数应该是这样的:但是,根据您在函数中的逻辑,循环 while 将执行两次。这是因为变量是在使用参数扩展
path_before
为变量设置另一个值之前设置的。result
ksh93中的
${var//pattern/replacement}
运算符(zsh、bash 和 mksh 也支持)仅替换模式的非重叠出现。${var//xxx/y}
变成,如果它变成xxxxxx
替换其中的 4 个重叠出现,那将是相当混乱的。yy
yyyy
xxx
这里
$PATH
表示一个目录列表(~
在这方面是~
当前工作目录的子目录,改成$HOME
是错误的)许多 shell(csh、tcsh、zsh、fish、yash)将其映射到它们的数组变量之一。
例如,在 zsh 中,从
$PATH
(映射到$path
数组,如 incsh
)中删除所有出现的目录只是一个问题:(或
path=( "${path[@]:#$dir}" )
保留空元素,但您不希望 中的空元素$PATH
)。bash
不这样做,但您可以$PATH
使用 split+glob 运算符在此处转换为数组:在 bash 中,就像在 ksh93 或 zsh 中一样,
${var//pattern/replacement}
可以使用语法应用于数组的所有元素"${array[@]//pattern/replacement}"
,但这无济于事,因为它无法删除元素,只需修改它们即可。因此,在 中
bash
,您只需要循环遍历元素:(
local -
,从 Almquist shell 复制以更改set -o
函数本地的选项(如 noglob)需要相对较新版本的 bash,不适用于您似乎正在使用的古老 3.2 版本)。要通过修改
:
存储在 -separated 列表中的元素来删除元素$PATH
,您需要每个$to_remove
:$to_remove:
在开头找到的 a。:$to_remove:
_:
_:
:$to_remove
结尾$PATH
仅包含$to_remove
,那么您将无法选择,因为空$PATH
意味着在当前目录中搜索命令,这是您想要的最后一件事。最好将其作为错误处理,或者可以将其视为在现实生活中通常不会发生的病态案例(如上)。或者您可以/dev/null
确保$PATH
查找找不到任何内容。所以:
你的前导冒号太多了。尝试不:
你会看到它一次性删除了所有出现的事件,不需要循环。请注意,摆弄
PATH
系统变量可能会使您的会话无法使用!