0

In a bash shell, consider this:

$ x="-name 'foo bar'"
$ find $x
find: paths must precede expression: `bar''
$ find -name 'foo bar'
./foo bar

What can I put in $x to make find $x behave like find -name 'foo bar'?

oskark
  • 21

2 Answers2

1

Do not put things that are separate (-name and foo bar) into a single string.

Instead, use an array:

args=( -name 'foo bar' )

find . "${args[@]}"

The quotes around the expansion of the array variable ensures that each element is individually quoted. This ensures that foo bar is a single argument.

You may also use the positional parameters:

set -- -name 'foo bar'

find . "$@"

Again, the quoting of "$@" are essential.

When you put -name 'foo bar' in your variable and use it unquoted with find, the shell will split the value on spaces, tabs and newlines. This means that tho find utility gets the arguments -name, 'foo, and bar'. Since the bar' argument is unknown to find, it complains.

If you had globbing patterns in your string, as in -name '*.txt', then the shell would split that into -name and '*.txt', and would then use '*.txt' as a globbing pattern to expand names in the current directory.

Kusalananda
  • 333,661
0

Note: Linux supports that find syntax, but it's not universal. It does not work on most BSD derivatives, for example, yielding the following:

bash-3.2$ find -name 'foo bar'
find: illegal option -- n
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
       find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]

Your problem isn't location, it's quoting and quote interpretation. Could you replace your code with

x='foo bar'
find -name "$x"

Alternatively, you could potentially try using eval:

x="-name 'foo bar'"
eval "find $x"

Which at least on my system resulted in:

bash-5.1$ x="-name 'foo bar'"
bash-5.1$ eval "find $x"
./foo bar

Which I believe is the desired result.