Bourne (and Bourne again) shell has complicated rules from line parsing to command execution.
For the sake of simplification, let's reduce your direct command line to
su -c 'whoami; ls'
and your "embedded" ones to
MYVAR="su -c 'whoami; ls'"
$MYVAR
In the first case, the command line is split in 3 tokens (words), because the space character is a metacharacter delimiting words, and also because quote escapes metacharacters (so is escaped the space into quotes). Before command execution, a quote removal stage applies, leaving 3 words and no quotes. The first word is the command name su
, and the 2 others are -c
and whoami; ls
, the command parameters. Right.
In the second case, the call consists of a single parameter expansion. For some expansions (including parameter expansion), word splitting applies. To that aim, a special variable named IFS is used to deduce words from the expanded parameter value. By default, IFS consists of space and tab characters, and newline. That means that each of these characters are used to split bunch of characters to constitute words. Here, the expanded value is:
su -c 'whoami; ls'
and we end up with 4 words, namely: su
, -c
, 'whoami;
and ls'
. On top of that, the quote removal stage does not occur for parameter expanded values. Command is issued (with command name su
and its 3 arguments), and you obtain a weird error message.
The complexity of this situation is that we have to keep a word containing a space as an argument to su
, the space itself being exposed to the word splitting stage, and quotes are useless in parameter expansion.
What you can do to deal with this is to play with the IFS variable. One solution would be:
IFS=: MYVAR="su:-c:whoami; ls"
$MYVAR
IFS is here overriden to exclude the space character from word splitting, the task being assumed by the newly introduced colon character.