Reputation:
Trying to run commands defined in variables inside a for loop:
somevar="Bit of text"
cmd1="command \"search '$somevar' here\""
cmd2="command \"search '$somevar' there\""
for cmd in cmd1 cmd2 ; do
eval \$$cmd
ssh server1 eval \$$cmd
done
I've put in the variations I have to consider such as the ssh inside the loop etc as these are needed in my script. I think the eval is the right direction, but the way that the quotes inside the command get interpreted comes through wrong.
Upvotes: 3
Views: 15756
Reputation: 22939
Consider this broken example:
$ cmd1="touch \"file with spaces\""
$ $cmd1
Quoting is handled before $cmd1
is expanded, so instead of one file this will create three files called "file
, with
, and spaces"
. One can use eval $cmd
to force quote removal after the expansion.
Even though it uses eval
, the line eval \$$cmd
has that same quoting problem since \$$cmd
expands to $cmd1
, which is then evaluated by eval
with the same behaviour as the broken example.
The argument to eval
must be the actual command, not the expression $cmd1
. This can be done using variable indirection: eval "${!cmd}"
.
When running this through SSH there is no need for the eval
because the remote shell also performs quote removal.
So here is the fixed loop:
for cmd in cmd1 cmd2 ; do
eval "${!cmd}"
ssh server1 "${!cmd}"
done
An alternative to indirection is to iterate over the values of cmd1
and cmd2
instead of their names:
for cmd in "$cmd1" "$cmd2" ; do
eval "$cmd"
ssh server1 "$cmd"
done
Upvotes: 5
Reputation: 18821
I see two solutions, either you change your loop to:
for cmd in "$cmd1" "$cmd2" ; do
ssh server1 $cmd
done
or to:
for cmd in cmd1 cmd2 ; do
ssh server1 ${!cmd}
done
Upvotes: 1
Reputation: 784958
Instead of eval \$$cmd
you need to use:
res=$(eval "$cmd")
ssh server1 "$res"
Upvotes: 0