Execute a command stored into a $variable

I made a script where I wrote:

COMMAND="/usr/bin/exiftool $PATH_NAME"
.... code ....
$COMMAND

The variable $PATH_NAME is assigned dynamically inside a while loop. The command works fine until it encounters files with spaces (for example PATH_NAME="Add Driver.png"). The console output is:

File not found: ./Add
File not found: driver.png

The command should be:

/usr/bin/exiftool ./Add driver.png

I think the problem is given by the spaces in the $PATH_NAME. I tried also to execute directly the command:

eval "/usr/bin/exiftool $PATH_NAME"

But same output error. Any idea to solve the problem? thanks.


Solution 1:

Instead of using simple strings, build your command using arrays. Arrays provide a convenient interface: If a is an array, then "${a[@]}" (note the quotes) expands into each element of a, without additional field splitting or globbing (so spaces, and things like wildcards, should remain intact).

Example:

$ a=(printf "|%s|\n" "foo   bar" "*")
$ echo ${a[@]}
printf |%s|\n foo bar bin boot dev etc home lib lib64 lost+found mnt opt proc root run sbin srv sys tmp usr var

Note how the * was expanded, and how the extra spaces between foo and bar were lost. But with the "${a[@]}", these are preserved:

$ echo "${a[@]}"
printf |%s|\n foo   bar *

This is ideal for building commands. Now, you can do:

$ "${a[@]}"
|foo   bar|
|*|

See? The arguments were retained perfectly.

So, do:

COMMAND=(/usr/bin/exiftool "$PATH_NAME")
"${COMMAND[@]}"

Solution 2:

glenn jackman's point is well taken. But, to solve your immediate use case, how about backticks? Like so:

`echo $COMMAND`

For example, this works:

COMMAND='ls /'
`echo $COMMAND`

Solution 3:

For more detailed explanations about how Bash interprets spaces, I recommend to read this: Bash variables and command substitution

Clean solution

Expanding a variable can lead to unexpected and sometimes catastrophic results if the variable contains special characters:

user@host:~$ do_something $some_variable

Expanding a variable within double-quotes can prevent such problems:

user@host:~$ do_something "$some_variable"

Explanation

The case encountered here is described at the end of the post:

The dangers of unquoted variables

In an ideal world, everyone would keep their string values short and without space/newline, or any other special characters.

[...]

But when people start adding special characters to filenames, such as spaces, expanding variables, without the use of double quotes, can be dangerous.

[...]

So the main takeaway here is: double-quote your variable references whenever possible.