find command with parameters in variable
Solution 1:
your problem is the simple quotes are not interpreted as such, but as being in your parameters.
You think you've executed this:
find /etc -name 'shells'
When in fact you've executed this:
find /etc -name \'shells\'
Keep it in mind: In bash, simple quotes inside double quotes are not ignored.
So the solution is to not put any simple quotes:
SEARCH="-name shells"; find /etc $SEARCH
A better solution is to use quotes and then use eval:
SEARCH="-name 'shells'"; eval " find /etc $SEARCH"
Security problem: Never, Ever use user-supplied information in an eval argument.
Solution 2:
try
eval find /etc $SEARCH
- eval will eval the line after variable expansion
Solution 3:
I'd strongly recommend avoiding eval
-- it tends to work great in simple tests, but then in production some unexpected shell metacharacter(s) show up and cause havoc. And if you have user input going into the string, it's virtually guaranteed that you'll wind up with security problems. Consider what'd happen if someone could add x'$(rm /somethingimportant)'y
to your file pattern list.
For situations like this where you're building a command dynamically, bash arrays are a much better way to go. Use something like:
namepatterns=(-name xxxx)
namepatterns+=(-o -name yyyyy -o -name "*.txt") # quotes prevent wildcard expansion
while read pattern; do
namepatterns+=(-o -name "$pattern")
done <patternfile.txt
find /etc "${namepatterns[@]}"
The "${array[@]}"
idiom treats each element of the array as a shell word, without any problematic parsing (word splitting, wildcard expansion) that an unquoted variable reference would suffer from.
BTW, I cheated slightly in the example above by adding the first -name xxxx
primary to the array without a -o
in front of it. If you were building the array entirely in a loop, this would be trickier:
namepatterns=()
while read pattern; do
namepatterns+=(-o -name "$pattern")
done <patternfile.txt
# At this point the array starts with "-o" -- not what you want
# So use array slicing to remove the first element:
namepatterns=("${namepatterns[@]:1}")
find /etc "${namepatterns[@]}"
See BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail! for more discussion and options for building and storing commands.