Skip to content
Will Holen edited this page Sep 8, 2020 · 11 revisions

Remove surrounding $() to avoid executing output (or use eval if intentional).

Problematic code:

if $(which epstopdf)
then
  echo "Found epstopdf"
fi

or

make_command() {
  printf 'cat header %q footer > %q\n' "$1" "$2" | tee log
}
$(make_command foo.html output/foo.html)

Correct code:

if which epstopdf
then
  echo "Found epstopdf"
fi

or

make_command() {
  printf 'cat header %q footer > %q\n' "$1" "$2" | tee log
}
eval "$(make_command foo.html output/foo.html)"

Rationale:

ShellCheck has detected that you have a command that just consists of a command substitution.

This is typically done in order to try to get the shell to execute a command, because $(..) does indeed execute commands. However, it's also replaced by the output of that command.

When you run echo "The date is $(date +%F)", bash evalutes the $(..). The command then becomes echo "The date is 2015-04-29", which writes out the string The date is 2015-04-29

The problem is when you use $(date +%F) alone as a command. Bash evaluates the $(..), and the command then becomes 2015-04-29. There is no command called 2015-04-29, so you get bash: 2015-04-29: command not found.

Sometimes this results in this confounding command not found messages. Other times you get even stranger issues, like the example problematic code which always evaluates to false.

The solution is simply to remove the surrounding $(). This will execute the command instead of the command's output.

If you instead have based a script around generating shell command strings, you can use eval "$(cmd)" to evaluate the output of a command as a second command. The benefit of using eval is that quotes, pipes, redirections, and other shell constructs will work as expected. Without the eval, all these will be treated as literal strings instead.

Exceptions:

None.

Related resources:

Clone this wiki locally