In Bourne-like shells (except zsh
), leaving a variable expansion unquoted in list context is the split+glob operator. In:
cond="-name '*.txt'"; echo $cond
The content of $cond
is first split according to the value of the $IFS
special variable. By default, that's on ASCII space, tab and newline characters.
So that's split into -name
and '*.txt'
. Then the glob part is applied. For each of those words that contain wildcard characters (here *
in the second one), the word is expanded to the list of files that match that pattern or left untouched if no file is matched. So, '*.txt'
would expand to the list of files in the current directory whose name starts with '
and end in .txt'
or to '*.txt'
if no file matches.
That list of words is then passed as separate arguments to echo
. If no file matched, that means echo
will receive 3 arguments: "echo"
, "-name"
and "'*.txt'"
.
Of course, the same applies for the find
command.
You want the find
command to receive the arguments -name
and *.txt
, so you need to tune your split+glob operator.
You need the value of $IFS
to match the separator you use in $cond
. So for instance:
cond='-name:*.txt'
IFS=':'
And you don't want the glob part of the split+glob operator, so you need to disable it with:
set -f
So it becomes:
cond='-name:*.txt'
IFS=:; set -f
find . $cond -ls
Or you could do:
cond='-name *.txt'
set -f
find . $cond -ls
and assume $IFS
has not been modified from its default value.
Alternatively, if your shell supports arrays, you may want to use that instead of relying on a scalar variables and expecting the shell to split it upon expansion.
This would work with ksh93
, mksh
, bash
or zsh
:
cond=(-name '*.txt') # an array with two elements: "-name" and "*.txt"
find . "${cond[@]}" -ls # elements of the array expanded as separate arguments
# to find.
cond="-name *.txt"
should be enough – Costas Nov 03 '14 at 11:30set -f ; find . $cond ; set +f
– Costas Nov 03 '14 at 11:43