强制 Bash 4 'globstar' 选项忽略符号链接

强制 Bash 4 'globstar' 选项忽略符号链接

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-releasebash-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...无论如何这都没有多大意义。)

相关内容