找到$(printf ... array)条件

miv*_*ivk 4 bash printf find

我正在尝试使用bash目录数组来排除find结果.但是,它没有像我期望的那样工作,我想理解为什么.

例:

mkdir -p findtest
cd findtest
mkdir -p A B C

exclude=(A C)

echo $(printf -- "-not -path '*/%s' " "${exclude[@]}")
Run Code Online (Sandbox Code Playgroud)

这打印-not -path '*/A' -not -path '*/C'.如果按字面意思使用它,它的工作原理:

find . -not -path '*/A' -not -path '*/C'
.
./B
Run Code Online (Sandbox Code Playgroud)

但是使用相同的命令替换,它不会从输出中排除任何目录:

find . $(printf -- "-not -path '*/%s' " "${exclude[@]}")
.
./C
./B
./A
Run Code Online (Sandbox Code Playgroud)

现在,如果我忽略要排除的路径名称周围的引号,它实际上是有效的:

find . $(printf -- "-not -path */%s " "${exclude[@]}")
.
./B
Run Code Online (Sandbox Code Playgroud)

但这当然没有用,因为某些目录名中有空格要排除.此外,我真的不明白为什么会有任何区别.

Tom*_*ech 5

set -x 得到了"为什么不起作用"这个问题的答案?:

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

好吧,弄清楚所有这些引用的内容是很痛苦的,但这足以看出命令不是你想要的.

重要的是,由字符串生成的单引号printf不是语法,即它们不表示参数的开始和结束.相反,它们被视为文字引号,因此find命令排除以引号开头和结尾的目录.

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

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

find . "${args[@]}"
Run Code Online (Sandbox Code Playgroud)