1

I am trying to construct a compound -name primary for the find command from an arbitrary number of strings in an array, of the form \( -name ${a[0]} -or -name ${a[1]} -or -name ${a[2]} ... \). While explicitly typing the name primary into the terminal works, storing it in a variable and recalling it by parameter expansion does not. Since expansion will take place before find runs, I assume the problem has something to do with quoting of the expanded variable.

Below is a minimal example for n=2 names, either typed literally or recalled from a variable. While I realize that I could pipe to grep, I would prefer to do everything with find in this case.

$ ls
a1  a2  b1  b2  c1  c2

$ find . \( -name a\* -or -name b\* \)
./a1
./a2
./b1
./b2

$ names="\( -name a\* -or -name b\* \)"

$ printf "%s\n" "$names"
\( -name a\* -or -name b\* \)

$ find . $(printf "%q" "$names")
find: -name\: unknown primary or operator

$ find . $(printf "%s" "$names")
find: \): unknown primary or operator

$ find . $names
find: \): unknown primary or operator

$ find . "$names"
find: \( -name a\* -or -name b\* \): No such file or directory
user001
  • 3,698

1 Answers1

2

Each -name, each -or, each parenthesis and each pattern must be a separate argument to find. Don't concatenate the arguments with spaces in between: you can't go back from there to a list of arguments — how could you know which spaces are there to separate arguments and which spaces are part of an argument? See also Why does my shell script choke on whitespace or other special characters?

If your shell supports arrays (ksh, bash, zsh), store the list of arguments in an array.

names=(\( -name a\* -or -name b\* \))
…
find "${names[@]}"

If your shell doesn't support arrays, you can construct the list in the positional arguments (the arguments that you can access as $1, $2, …). The obvious limitation is that you can't use the positional arguments for another purpose.

set -- \( -name a\* -or -name b\* \)
…
find "$@"