bash command substitution: find command with wildcard in single quotes
-
06-06-2021 - |
Question
I want to dynamically construct a "find" command and see whether it returns anything.
This is the minimised example. In a directory containing files,
find . -name '*'
of course returns the files. But
VAR="find . -name '*'"
$VAR
returns nothing. Looks like a problem of interpretation of quotes. But why?
(The real case is that I want to see whether files with a certain prefix exist. So I intended to use -name '${MYPREFIX}*'
, capture the output within $()
, and test whether the resulting string is empty.)
Apologies if this is trivial. I did spend time to find the answer, but all cases I found were somehow different. Usually, the problem is the shell expanding the wildcard when the find command should do it. Here, the shell does not do any premature expansion, but neither does find, maybe because find receives literal quotes? Thanks for any insight.
Solution
eval $VAR
The eval
will reinterpret the value of $VAR
as a command with arguments.
Be wary: eval
is a powerful but dangerous mechanism.
OTHER TIPS
I recommend against using eval
whenever possible. In this case, if you really need to store the command in a variable before executing it, I'd recommend using an array and then invoking it as "${cmd_array[@]}"
:
var=(find . -name "${prefix}*") # This stores the elements of the command as an array
result=$("${var[@]}") # This executes the command and stores its output in $result
Note that you can also construct the find command dynamically:
patterns=() # start with an empty list of patterns
for prefix in $prefix_list; do
patterns+=(-o -name "${prefix}*") # add each prefix to a list of find patterns
done
patterns=("${patterns[@]:1}") # Use array slicing to remove the first "-o"
result=$(find . "${patterns[@]}")
Depending on how you use it, it might also help to wrap the find command in a bash function:
find_prefix() {
find . -name "${1}*"
}
result=$(find_prefix "$prefix")