我需要在生成的一组“起点”上调用 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))
!
即使这些文件类似或-name
会find
阻塞,这也有利于工作。
但是,如果这些文件是指向目录的符号链接(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 的单引号参数):
- 导入必要的模块
os
并sys
在命令中使用 - 遍历参数
a b
,xyz
并abc
使用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 命令之前过滤所有有效路径,因此不会传递无效路径。