查找一组可能不存在的目录

查找一组可能不存在的目录

我需要在生成的一组“起点”上调用 find ,但某些路径可能无效:

paths() {
    #
    # mock version of the generator
    #
    echo /bin
    echo kjsdfhk
    echo /etc
}

find $(paths) -type f name foo.sh

我的问题是我不知道该路径是否有效,如果无效,我想默默地忽略它。对我来说最容易的是现在

paths \
  | while read path;
    do
        test -e "$path" || continue
        find "$path" -type f -name foo.sh
    done

但这很昂贵:它调用寻找对于每个有效路径,并且由于整个代码可能在循环内调用,我想找到一种更有效的方法。

一种简单且非常unix-y的解决方案是寻找从 STDIN 读取“起点”:

paths \
  | while read path;
    do
        test -e "$path" || continue
    done \
  | find - -type f -name foo.sh

除了那个...寻找不支持这个! :)

有任何想法吗?

请注意,路径是由用户提供的,因此需要考虑空格(可能还有其他有趣的事情)。另外,我的目标是 POSIX /bin/sh,但这可能会被牺牲。 (哦,将整个 STDERR 下沉/dev/null也不是一个选择......)

更新:抱歉,我忘了提及我只能使用 bash——我可能会因为 POSIX 注释而更加困惑。实际上,代码正在寻找 Bash 脚本中的源代码片段,现在大部分位于我可能会在未来的版本中摆脱一些羞辱。因此,如果我可以避免添加更多的 bashisms,那会很酷,但我肯定买不起“zshisms”,无论多么优雅——正如 @stephane 的答案(现在就投票吧!)。

答案1

在 中zsh,如果数组中有路径,如下所示:

files=($(paths))

paths(这将分割on space、tab newline 或 nul的输出)或:

files=(${(f)"$(paths)"})

要分割线,你可以这样做:

find $^files(N) -type f -name foo.sh

或者,如果您想限制为目录:

find $^files(/N) -type f -name foo.sh

现在,如果这些文件都不存在,您最终可能会运行:

find -type f -name foo.sh

对于某些find实现(例如 GNU 的实现)意味着在当前目录中搜索。为了避免这种情况,你可以这样做:

dirs=($^files(/N))
(($#dirs)) && find $dirs -type f -name foo.sh

或者:

setopt cshnullglob
find $^files(/) -type f -name foo.sh

现在,有了zsh,这里就不再需要了find,您可以简单地执行以下操作:

files=($^files/**/foo.sh(.N))

!即使这些文件类似或-namefind阻塞,这也有利于工作。

但是,如果这些文件是指向目录的符号链接(find不会在其中查找文件,而zsh会(这实际上可能是您想要的)),则情况会有所不同。

答案2

由于您使用的是 bash,因此将路径列表存储在大批。迭代数组以构建现有路径的数组。如果结果数组为空,则确实需要特殊情况,否则find会出错或遍历当前目录。

为了完全健壮,请确保没有任何路径参数以 a 开头-,否则find会解释为选项或主要参数。

paths=(/some/where around/here -print 'one  with
odd spaces')
existing_paths=()
for x in "${paths[@]}"; do
  if [ -e "$x" ]; then
    if [[ "$x" = -* ]]; then x="./$x";; fi
    existing_paths+=("$x")
  fi
done
if [[ ${#existing_paths[@]} -ne 0 ]]; then
  find "${existing_paths[@]}" -type f -name foo.sh
fi

答案3

您可以使用 python 测试路径是否存在,回显以 NUL 分隔找到的路径并将它们输入xargs以传递给find,除非 python 输出的长度超过应仅调用 find 一次的最大参数长度:

python -c 'import os, sys;  sys.stdout.write("\0".join([x for x in sys.argv[1:] if os.path.exists(x)]) + "\0")' a\ b xyz abc| xargs -0 --no-run-if-empty find

python 部分(-c 的单引号参数):

  • 导入必要的模块ossys在命令中使用
  • 遍历参数a bxyzabc使用for x in sys.argv[1:]
  • 仅当路径存在时才放入列表中:[x ... if os.path.exists(x)]
  • 使用 NUL 加入列表并附加 NUL 并将其写出到 stdout: sys.stdout.write("\0".join[...] + "\0")

如果你有一个空目录,touch a\ b xyz你会看到前两个参数已找到,但abc没有找到,并且 find 永远不会传递到后一个路径。

答案4

如果这对你有用

查找 $(paths) -type f 名称 foo.sh

那么您可以更改路径函数以仅回显有效路径,例如

if [ -d "$path" ]; then echo $path fi

或者如果路径也可以是一个文件

if [ -d "$path" ] || [ -e "$path" ]; then echo $path fi

这样find就只会被执行一次。如果您无法修改路径函数,您始终可以创建另一个函数,该函数将在将所有有效路径传递给 find 命令之前过滤所有有效路径,因此不会传递无效路径。

相关内容