2

I need a lot of different aliases to create ssh tunnels to different servers. To give you a few of them:

alias tunnel_1='autossh -M 20000 -N -L 8080:localhost:8080 -N -L 9200:localhost:9200 -N -L 8090:localhost:8090 user@server1.xx'
alias tunnel_2='autossh -M 20000 -N -L 8000:localhost:8080 -N -L 9200:localhost:9200 -N -L 8090:localhost:8090 user@server2.xx'

I came up with this function I added in my aliases :

addPort () {
  echo "-N -L $1:localhost:$1 "
}

tunnel () {
  aliasString="autossh -M 20000 "
  for port in "${@:2}"
  do
    aliasString+=$(addPort $port)
  done
  aliasString+="$1"
  eval $aliasString
}

so I just need to do this to tunnel to the server I want:

tunnel abc@domain.com 8080 9000 7200

It’s working well, But I’d like not to use eval if it’s possible. is there another way to call autossh directly and give it the correct params without using eval?

Albizia
  • 201
  • 1
    You don't need to add -N along with each tunnel. BTW, do you consistently tunnel the same ports to a bunch of different servers? If so, how about creating a wildcard Host entry in ~/.ssh/config, with the appropriate LocalForward directives? – Gordon Davisson Sep 10 '19 at 21:41

2 Answers2

5

Use a single shell function:

tunnel () {
    local host="$1"; shift
    local port
    local args

    args=( -M 20000 )

    for port do
        args+=( -N -L "$port:localhost:$port" )
    done

    autossh "${args[@]}" "$host"
}

or, for /bin/sh:

tunnel () {
    host="$1"; shift

    for port do
        set -- "$@" -N -L "$port:localhost:$port"
        shift
    done

    autossh -M 20000 "$@" "$host"
}

Both of these functions extracts the first argument into the variable host, and then builds a list consisting of strings made up from the provided port numbers.

At the end, both functions invoke autossh with the given list and the host.

Kusalananda
  • 333,661
  • How does port get initialized to $2 $3 … ? – Albizia Sep 10 '19 at 21:39
  • 1
    @Albizia The loop loops through them. You could write the loop as for port in "$@"; do ...; done too, if that feels better (but then you have to remember to double quote $@ as "$@"). – Kusalananda Sep 10 '19 at 23:03
4

aliasString doesn't contain any shell special characters other than whitespace. So eval $aliasString, eval "$aliasString" and $aliasString are all equivalent. The unquoted variable substitution aliasString performs word splitting and wildcard expansion; since there are no wildcards, that's just word splitting. eval "$aliasString" performs all the shell parsing and evaluation steps; since there are no special characters other than whitespace, the only thing that happens is word splitting. eval $aliasString does the word splitting from the unquoted variable substitution, then joins the words together with a space in between, and then does the whole shell evaluation which splits the words again.

Here's a simpler way to write this function in plain sh.

tunnel () {
  command=$1
  shift
  for port do
    command="-N -L "$port:localhost:$port $command"
  done
  autossh -M 20000 $command
}

Note that this only works with parameters that don't contain any whitespace wildcard characters. See Kusalananda's answer for more robust methods.

Shell aliases are not involved at all.