9

I'm trying to pass a variable to ssh remote but not works. My code is:

#!/bin/bash
set -x
conexion="user@xx.yy.zz.pp"
parameter="$1"
ssh -T $conexion <<'ENDSSH' 
clear
echo "$parameter"
ENDSSH

I execute:

./script.sh try

It says me:

parameter: Undefined variable.

any help please?

5 Answers5

14

Passing variables (environment variables) over ssh is possible but generally restricted.

You need to tell the client to send them. For instance with OpenSSH, that's with:

ssh -o SendEnv=parameter host cmd...

But you also need the server to accept it (AcceptEnv configuration directive with OpenSSH). Accepting any variable is a big security risk so is generally not done by default, though some ssh deployments allow some variables under some namespace (like LC_* in some OpenSSH deployments).

You also need to export the variable before calling ssh, like:

LC_parameter="$parameter" ssh -o SendEnv=LC_parameter host csh << 'END'
echo $LC_parameter:q
END

Above, we're passing the content of the $parameter bash shell variable as the LC_parameter environment variable to ssh. ssh sends that over to sshd, which if it accepts it, passes it as an environment variable to the login shell of the user which then passes it to that csh command (which can then expand it).

But as mentioned earlier, that won't work unless the administrator of the host machine has added a AcceptEnv LC_parameter or AcceptEnv LC_* (that one sometimes done by default) to the sshd configuration.

The Undefined variable error message in your example suggests the login shell of the remote user is csh or tcsh. It's better to explicitly invoke the shell to avoid surprises (ssh host csh also means a tty is not requested so you don't need -T). Note the $LC_parameter:q syntax, which is the csh way to pass the content of a variable verbatim, not "$LC_parameter" which doesn't work if the variable contains newline characters.

If using LC_* variables is not an option, then alternatively, you can have the client shell (bash in your case) expand the variable. A naive way would be with

ssh host csh << END
echo "$variable"
END

But that would be dangerous as the content of the variable would be interpreted by the remote shell. If $variable contains `reboot` or "; reboot; : " for instance, that would have bad consequences.

So, you'd need first to make sure the variable is properly quoted in the syntax of the remote shell. Here, I would avoid csh where it's hard to do reliably and use sh/bash/ksh instead.

Use a helper function to do the sh quoting:

shquote() {
  awk -v q=\' -v b='\\' '
    BEGIN{
      for (i=1; i<ARGC; i++) {
        gsub(q, q b q q, ARGV[i])
       printf "%s ", q ARGV[i] q
      }
      print ""
      exit
    }' "$@"
}

And call ssh as:

ssh host sh << END
parameter=$(shquote "$parameter")
echo "\$parameter"
END

See how we escape the third $ so the expansion of $parameter is done by the remote shell, not the local one.

  • no need to be environment variables, variables can be expanded, and passed as text. This seems to be the intention, and is what is done in @ddnomad's answer. – ctrl-alt-delor Jul 29 '16 at 10:49
  • Good explanation and use of SendEnv and AcceptEnv as using these properly passes a variable rather than a value. This is, strictly speaking, what was asked for. – Kusalananda Jul 29 '16 at 11:02
  • 1
    @richard, but as I say, that naive approach is unreliable and dangerous. My answer also gives details on how to do that expansion reliably/safely – Stéphane Chazelas Jul 29 '16 at 11:14
  • 1
    @Kusalananda, a systems analyst must not answer what the customer was “strictly speaking”, they have to work out what they want, often with a series of questions. I have been in many situations, where a customer will ask for something, using words in the domain of programmer/software engineer/analyst, with no idea what the words really mean, and them members of my team will take there words at face value and deliver them something that does not swing. – ctrl-alt-delor Aug 11 '16 at 16:16
4

Among other tricks (like passing LC_* environment variables) you can do the following:

PARAMETER="123"
ssh user@host PARAMETER="$PARAMETER" bash -s <<- 'EOF'
    echo $PARAMETER
EOF

The advantage of the approach is absence of the requirement to export PARAMETER, adding its name to AcceptEnv (in case of names not starting from LC_) in config /etc/ssh/sshd_config on remote host, adding to SendEnv on local host (to -o or to /etc/ssh/ssh_config).

2

Removing quotes from ENDSSH on the 4th line should help.

Courtesy: @Archemar in comments to the question.

WARNING As it was stated by @StéphaneChazelas in the comment to this answer, this sollution would cause the $parameter variable in the here doc to be expanded by the local shell, which means the content of the variable would be interpreted as shell code by the remote shell, which means it introduces a command injection vulnerability. So generally it's discouraged to do so.

ddnomad
  • 1,978
  • 2
  • 16
  • 31
  • 2
    That would cause the $parameter variable in the here doc to be expanded by the local shell, which means the content of the variable would be interpreted as shell code by the remote shell, which means it introduces a command injection vulnerability for instance if the value of $parameter is not entirely under your control and you've not sanitized it. – Stéphane Chazelas Jul 29 '16 at 11:12
  • @StéphaneChazelas good point I would add a warning to the answer – ddnomad Jul 29 '16 at 11:15
1

You can use printf %q:

#!/bin/bash
set -x
conexion="user@xx.yy.zz.pp"
parameter="$1"
ssh -T $conexion parameter="$(printf %q "$parameter");" <<'ENDSSH' 
clear
echo "$parameter"
ENDSSH

Just to get this in order: parameter="$(printf %q "$parameter");" ... is simply setting a variable before a command as in x=5; echo $x but it uses printf %q which quotes the $parameter safely.

I like this solution because it does not depend on the trick sending environment variables and therefore does not depend on the AcceptEnv setting on the ssh server.

Matmarbon
  • 245
0

Insert single quotes inside double quotes:

$ file_name="one two"
$ ssh remote ls "'$file_name'"
ls: cannot access 'one two': No such file or directory

The variable gets substituted first, without removing single quotes. This way the content is sent to ssh single-quoted.