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 source
ing it, it runs in the shell you source
d it from, whether that's bash, zsh, dash, or whatever.