4
> find /etc -name 'shells'
/etc/shells     # good !!

> SEARCH="-name 'shells'"; find /etc $SEARCH
# nothing found - bad !!

Why "find" command can't take params in variable?

Other commands work fine in such mode. It is probably related with spaces and parsing. How can I at first construct params in variable and then execute "find" with this params?

To be clear, I want to make the chain of -name xxxx -o -name yyyyy -o -name zzzzz and then find all the files by one run

4 Answers 4

8

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.

1
  • Thanks, that is the first, best and shortest answer to this topic on the net.
    – abu_bua
    May 1, 2022 at 0:29
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.

2

try

eval find /etc $SEARCH
  • eval will eval the line after variable expansion
0

Try the -name option like this :

SEARCH="shells"; find /etc -name $SEARCH

Take a look at this question for some options.

2
  • Sorry, not suitable. I want to make the chain of -name xxxx -o -name yyyyy -o -name zzzzz in loop
    – xoid
    Jul 21, 2015 at 7:11
  • ok added link for you
    – LenW
    Jul 21, 2015 at 7:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .