bash - 使用 $(printf ... array) 条件查找
问题描述
我正在尝试使用 bash 目录数组从find
结果中排除。但是,它没有按我的预期工作,我想了解原因。
例子:
mkdir -p findtest
cd findtest
mkdir -p A B C
exclude=(A C)
echo $(printf -- "-not -path '*/%s' " "${exclude[@]}")
这打印-not -path '*/A' -not -path '*/C'
。如果从字面上使用它,它可以工作:
find . -not -path '*/A' -not -path '*/C'
.
./B
但是使用相同的命令替换,它不会从输出中排除任何目录:
find . $(printf -- "-not -path '*/%s' " "${exclude[@]}")
.
./C
./B
./A
现在,如果我省略要排除的路径名周围的引号,它实际上可以工作:
find . $(printf -- "-not -path */%s " "${exclude[@]}")
.
./B
但这当然无济于事,因为某些目录名称中有空格要排除。此外,我真的不明白为什么会有任何不同。
解决方案
set -x
对“为什么它不起作用”这个问题有答案?:
$ set -x
$ find . $(printf -- "-not -path '*/%s' " "${exclude[@]}")
++ printf -- '-not -path '\''*/%s'\'' ' A C
+ find . -not -path ''\''*/A'\''' -not -path ''\''*/C'\'''
好吧,弄清楚所有这些引号发生了什么是很痛苦的,但足以看出该命令不是您想要的。
重要的是,由 产生的字符串中的单引号printf
不是语法的,即它们不表示参数的开始和结束。相反,它们被视为文字引号,因此 find 命令不包括以引号开头和结尾的目录。
一种解决方案是使用循环来构建您的命令:
args=()
for e in "${exclude[@]}"; do
args+=( -not -path "*/$e" )
done
find . "${args[@]}"