bash build list of options for find

1694 views arrays
3

I am trying to build a list of options in a bash array for find to find certain files. Consider a directory with the following files:

A_foo_0
A_foo_1
A_bar_0
B_foo_0
B_foo_1
B_bar_0
C_foo_0
C_foo_1
C_bar_0

My script has programmatically determined that *foo* files should be found by find, but not files that start with A or C. So only B_foo_0 and B_foo_1 should be found, as the following manual invocation of find shows:

$ find ./ -name "*foo*" ! -name "A*" ! -name "C*"
./B_foo_1
./B_foo_0

But I need to learn the prefixes to exclude dynamically, so I have an array defining which prefixes should be excluded:

declare -a excludeArray
excludeArray+=(A)
excludeArray+=(C)

I want to turn that into effectively ! -name "A*" and ! -name "C*" and pass it to find, something like this:

excludeOptions=( "${excludeArray[@]/%/\*\"}" )  # Append *" to each element
excludeOptions=("${excludeOptions[@]/#/ ! -name \"}") # Prepend ! -name " to each element

Now if I echo that, it looks pretty good:

echo "${excludeOptions[@]}"
 ! -name "A*"  ! -name "C*"

But when I try to use it with find, I get the following:

$ find ./ -name "*foo*" "${excludeOptions[@]}"
find: paths must precede expression:  ! -name "A*"

Turning on the debugging mode shows that the issue is likely due to the quoting of the elements:

$ set -x
$ find ./ -name "*foo*" "${excludeOptions[@]}"
+ find ./ -name '*foo*' ' ! -name "A*"' ' ! -name "C*"'
find: paths must precede expression:  ! -name "A*"

$ find ./ -name "*foo*" ${excludeOptions[@]}
+ find ./ -name '*foo*' ' ! -name "A*"' ' ! -name "C*"'
find: paths must precede expression:  ! -name "A*"

If I build the array like follows, it works:

$ excludeOptions=( ! -name "A*" ! -name "C*")
$ find ./ -name "*foo*" "${excludeOptions[@]}"
+ find ./ -name '*foo*' '!' -name 'A*' '!' -name 'C*'
./B_foo_1
./B_foo_0

But I can't figure out how to do that dynamically.

answered question

1 Answer

9

There's no easy way to prepend or append separate words to the existing array contents en masse. You'll need a loop.

excludeOptions=()
for e in "${excludeArray[@]}"; do excludeOptions+=(! -name "$e*"); done

posted this

Have an answer?

JD

Please login first before posting an answer.