Bash 4 有一个很棒的选项,称为“globstar”,它模拟(即被窃取)zsh 的**
跨多个目录进行通配的语法。然而,它总是遵循符号链接,这在某种程度上削弱了它(至少对于我的使用而言)。
有没有办法阻止**
跟踪符号链接?如果没有,是否有计划在未来的版本中将此功能添加到 Bash 中?在那之前(或者如果没有这样的计划),任何人都可以提出一个体面的(且方便的)解决方法吗?
我想我已经读到zsh的实现更灵活并且没有这个问题,但不幸的是我不能直接切换到zsh,因为我们在我的工作场所源了很多bash脚本,而我没有时间或者将它们全部转换为 zsh 并保持最新的专业知识。 (我想可能可以启动一个 Bash 子 shell,获取所需的脚本,然后以某种方式将所有更改的环境变量、别名、函数等向后移植到 zsh 中,但我也不知道该怎么做。)
我知道我可以使用它编写一个函数find
,它会按照我想要的方式运行,然后**
为该函数添加别名,但是我无法执行类似的操作path/to/parent/**/child/paths
并使其正常工作;我能想到的最接近的是类似 的东西$(** path/to/parent child/paths)
,其中**
是一个函数的别名,该函数将父路径作为第一个参数,然后将其--wholename */child/paths/*
作为参数包含到find
它构造和执行的命令中。这既尴尬又丑陋,我真的想要更好的东西。 (特别是,我喜欢这种path/**/path
格式可以轻松地允许变化path/**
,并且**/path
轻松直观,而函数会将其分别转换为** path/
和** '.' /path
,其中第二个非常糟糕。)
有什么想法吗?
答案1
**
自 以来不再遵循符号链接bash-4.3
。
看变化之间:bash-4.3-release
bash-4.3-rc2
globstar (**) 不再遍历解析为目录的符号链接。这消除了一些重复的条目。
答案2
没有办法阻止**
bash 中的符号链接,也没有请求提供一种方式遇到任何回应。
如果您想编写代码,您可以修补 bash 以引入一个新选项,该选项可以关闭符号链接遍历**
。如果您将补丁提交给 bash 维护者,这将增加该功能有一天出现的机会。尽管如此,至少在此期间,您最终将不得不部署和维护关键系统程序的本地版本,这可能是一个坏主意。
没有办法改变**
别名的行为。如果您使用中间数组来存储扩展结果,则可以使用以下方法find
:
find . … -print0 | {
a=()
while read -r -d "" line; do
a+=("$line")
done
frobnicate "${a[@]}"
}
请注意,使用 输出的程序的整个部分find
必须位于管道的右侧,因为 bash 在子进程中执行管道的右侧。
如果可以排除名称包含换行符的文件,那就更容易了:
set -f; IFS=
a=($(find . … ! -name $'*\n*' -print))
set +f; unset IFS
frobnicate "${a[@]}"
您可以尝试在 zsh 的 ksh 模拟模式下运行现有脚本。 Zsh 并没有实现 bash 的所有功能——几乎所有 bash 的功能都存在于 zsh 中,但有时语法不兼容。您还可以尝试使用 ksh93 运行脚本:从 93o+ 开始的版本支持**
(如 zsh),并且大多数(但不是全部)bash 功能都来自 ksh,因此您的脚本可能很容易移植到 ksh93。切换到 ksh 还有一个额外的好处,那就是它往往比 bash 或 zsh 更快。
答案3
我找到了一个更简单的解决方法:只需使用 zsh 来评估这些 glob。
在我的 bash 配置脚本中,我有以下内容:
function zeval () {
echo $@ | zsh
}
alias z*='zeval echo'
现在,无论我在 zsh 中使用什么地方/path/with/**/glob
,我都可以在 bash 中执行以下操作:
$(z* sandbox/rmall_gdsarc/**/*gds)
由于扩展别名的上下文和需要通配符的上下文通常不会重叠,因此别名z*
不应有歧义。
(我能想到的别名z*
和 Bash glob 语法之间的混淆的唯一上下文是,如果您所在的目录已经在您的路径上,包含以“z”开头的可执行脚本,并且您想使用glob...无论如何这都没有多大意义。)