4

I have a problem with bash behavior while evaluating grave accents. I want to run find with optional -name:

find -name "*.log"

works fine, but when i want to get the -name part from nested script

find `echo "-name \"*.log\""`

it simply does not work. Running it under bash -x shows, that this command is evaluated to

find -name '"*.log"'

Apparently its because of this single quotes. Is there some workaround to get rid of them? Best regards.

//requested OS and bash version: Debian, 7.8 wheezy, Bash 4.2.37(1)-release

tm852
  • 43

2 Answers2

1

Bash has not added single quotes to the command. The single quotes that you see are added to make displayed command be valid bash syntax. If you see

find -name '"*.log"'

it means that bash was told to execute this command: find with two arguments -name and "*.log" (the double quotes being part of the argument). It could have been input exactly like this or in some other equivalent way like

find -name \"*.log\"
find -name "\"*.log\""
find -name '"*.'log""\"

Now let's look at what you wrote:

find `echo "-name \"*.log\""`

The command running in the subshell is

echo "-name \"*.log\""

Its output is

-name "*.log"

That's where the double quotes in the argument come from. When the output of a command substitution is interpolated into a command line, it is not parsed as shell syntax — so for example

find `echo '; rm -rf ~'`

will not delete all your files but instead pass ; as the first argument to find, rm as the second, -rf as the third and your home directory (assuming it doesn't contain any special characters) as the fourth.

When a variable substitution or a command substitution is outside double quotes, the following transformations happen to the variable's value or the command's output:

  1. For command substitution only: trailing newlines are stripped.
  2. The string is broken up into whitespace-delimited pieces. (You can configure the delimiters by setting the IFS variable.) Note that the result of this transformation is a list of strings.
  3. Each element of the list is interpreted as a wildcard pattern. If that pattern matches files, then the element is replaced by the list of matching files (this is called globbing). If the pattern doesn't match any file, the element is left alone.

Step 2 breaks the string -name "*.log" into two pieces: -name and "*.log". Neither piece matches any file so the result is the two words -name and "*.log".

What you meant to do is instead run find with the arguments -name and *.log. You can do this by turning off globbing:

set -f
find `echo -name *.log`
set +f

This is not robust: it won't let you pass file names containing whitespace, for example. Generally speaking, you should not attempt to build commands by storing bits of them in a variable or passing them around in commands. The best way to do what you're doing depends on what you're actually trying to do. Here's some general advice:

-1

Use function eval, this will resolve your issue

command="find `echo \"-name \"*.log\"\"`"   response=$(eval "$command")
muru
  • 72,889
g_jha
  • 19