1

I am running these two commands:

echo $(find ./ $OPT1 $OPT2 $OPT3)
echo find ./ $OPT1 $OPT2 $OPT3 | bash

and the weird thing is, the top command yields no results while the bottom one does. Why is that?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255

1 Answers1

4

The first runs find ./ $OPT1 $OPT2 $OPT3 word-splits and globs the output (no quotes around $()), and passes it to echo, which prints it. Which is mostly the same as just running the find command, but newlines get turned to spaces, and if any file names printed by find contain * or ?, they'll be expanded. Though if your IFS contains something unusual, the word-splitting will be similarly unusual. (Also, $OPT1 and others are also not quoted, so they're split and globbed before passing to find.)

The second prints find ./ $OPT1 $OPT2 $OPT3, expanding the variables (again splitting and globbing, because no quotes), and passes the result to bash, which runs it as a command. Again pretty much the same as just running the find, but if the variables contain shell metacharacters, they'll be expanded (again) by the second shell.

The first one, note that the filename foo* is expanded, globbing the all three:

$ touch foo1 foo2 foo\*
$ echo $(find .)
. ./foo2 ./foo* ./foo1 ./foo2 ./foo1

The second, note that the pipe to bash causes the inner variable to be expanded, like if you used eval :

$ OPT='echo $BASH_VERSION'
$ echo $OPT
echo $BASH_VERSION
$ echo $OPT | bash
4.3.30(1)-release
$ eval $OPT        # about the same  
4.3.30(1)-release

Variable expansion is probably the simplest example of this, but it needs an exported variable or one that the other shell has in any case, like BASH_VERSION here. (I can't come up with a sane example with find for now.)

Depending on what your OPT variables contain, the extra eval round might make the difference in if find matches anything.

ilkkachu
  • 138,973