用$(printf ... array)条件查找

时间:2018-07-18 11:42:30

标签: bash find printf

我正在尝试使用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

但这当然无济于事,因为某些目录名称中有空格要排除。而且,我真的不明白为什么会有什么不同。

2 个答案:

答案 0 :(得分:5)

set -x对“为什么不起作用”这个问题有答案?:

$ set -x
$ find . $(printf -- "-not -path '*/%s' " "${exclude[@]}")
++ printf -- '-not -path '\''*/%s'\'' ' A C
+ find . -not -path ''\''*/A'\''' -not -path ''\''*/C'\'''

好吧,弄清楚所有这些引号是怎么回事,但足以看出该命令不是您想要的。

重要的是printf产生的字符串中的单引号不是 syntactic ,即它们不表示参数的开始和结束。相反,它们被当作 literal 引号,因此find命令排除了以引号开头和结尾的目录。

一种解决方案是使用循环来构建命令:

args=()
for e in "${exclude[@]}"; do 
  args+=( -not -path "*/$e" )
done

find . "${args[@]}"

答案 1 :(得分:-1)

您正在动态地准备命令,即find命令是printf的输出。在这种情况下,您需要使用eval,以便外壳程序可以正确处理它。

eval find . $(printf -- "-not -path '*/%s' " "${exclude[@]}")