Em
cmd="bash -c 'echo hello'"
$cmd
Você não está executando o comando bash -c 'echo hello' , está executando o comando $cmd simple.
Esse $cmd não indicado significa chamar o operador split + glob. Aqui, com o valor padrão de $IFS , o conteúdo de $cmd é dividido em bash , -c , 'echo e hello' . Então, você está executando bash com esses 4 argumentos, é como se você tivesse digitado:
bash -c "'echo" "hello'"
E esse código 'echo tem uma cotação de fechamento ausente (o argumento hello' entra no $0 desse script sequencial).
Se você quiser avaliar o conteúdo de $cmd como código shell, é
eval "$cmd"
Então:
cmd="bash -c 'echo hello'"
foo=$(eval "$cmd")
echo "$foo"
Embora você também possa usar seu operador split + glob de maneira diferente:
cmd='bash,-c,echo hello'
IFS=, # split on comma
set -f # disable glob
foo=$($cmd)
echo "$foo"