Home > Mobile >  Bash not resolving variable
Bash not resolving variable

Time:11-24

Very new to bash. I'm trying to write a file which simplifies running an sshuttle command to one of a list of environments.

#!/bin/bash

declare -A environments

environments=(
    [dev]="dev.myhost.com 10.0.1.0/22 --dns"
    [qa]="qa.myhost.com 10.0.2.0/22 --dns"
    [prod]="prod.myhost.com 10.0.3.0/22 --dns"
)

vpn() {
    echo sshuttle --verbose -r ${environments[$1]}
    sshuttle --verbose -r ${environments[$1]}
}

The usage will be vpn qa, which should resolve to sshuttle --verbose -r qa.myhost.com 10.0.2.0/22 --dns

Unfortunately, when running vpn qa I get instead an error which indicates that the ${environments[$1]} isn't being resolved at runtime:

usage: sshuttle [-l [ip:]port] -r [user@]sshserver[:port] <subnets...>
sshuttle: error: at least one subnet, subnet file, or -N expected

The echo outputs what I am expecting to see, and copy-pasting that into the terminal works.

What am I missing? Thanks.

CodePudding user response:

zsh doesn't perform word-splitting on unquoted variable references like most other shells do. That means that when ${environments[$1]} expands to qa.myhost.com 10.0.2.0/22 --dns, that value will be treated as a single long argument (containing spaces) rather than as three separate arguments. The difference is not visible with echo, since that just puts spaces between separate arguments, but it matters a great deal to sshuttle. (This is yet another case where echo is misleading, and using set -x to show what's going on would be much better.)

In order to get zsh to split the value, you need to add an = modifier to the variable reference: ${=environments[$1]}. Of course, that's not compatible with bash, so if you want this to work in both bash and zsh, you need to do something like this:

environments=(
    [dev]="dev.myhost.com 10.0.1.0/22 --dns"
    [qa]="qa.myhost.com 10.0.2.0/22 --dns"
    [prod]="prod.myhost.com 10.0.3.0/22 --dns"
)

if [[ -n "$ZSH_VERSION" ]]; then
    # zsh-compatible definition of the function
    vpn() {
        echo sshuttle --verbose -r ${=environments[$1]}
        sshuttle --verbose -r ${=environments[$1]}
    }
else
    # bash-compatible definition of the function
    vpn() {
        echo sshuttle --verbose -r ${environments[$1]}
        sshuttle --verbose -r ${environments[$1]}
    }
fi

Note that the #!/bin/bash in your script is ignored (and misleading) -- since you're sourceing it, it runs in the shell you sourced it from, whether that's bash, zsh, dash, or whatever.

  •  Tags:  
  • bash
  • Related