137

I want to be able to run a command from my machine using ssh and pass through the environment variable $BUILD_NUMBER

Here's what I'm trying:

ssh [email protected] '~/tools/myScript.pl $BUILD_NUMBER'

$BUILD_NUMBER is set on the machine making the ssh call and since the variable doesn't exist on the remote host, it doesn't get picked up.

How do I pass the value of $BUILD_NUMBER ?

1
  • 1
    unrelated to Hudson, removed the tag. (Hudson just creates the variable) Commented Jul 23, 2010 at 14:02

8 Answers 8

256

If you use

ssh [email protected] "~/tools/run_pvt.pl $BUILD_NUMBER"

instead of

ssh [email protected] '~/tools/run_pvt.pl $BUILD_NUMBER'

your shell will interpolate the $BUILD_NUMBER before sending the command string to the remote host.

Sign up to request clarification or add additional context in comments.

7 Comments

If someone MUST use single quotes so that the command included in the quotes is not locally evaluated, then they should use "'$VARIABLE'". Example: ssh [email protected] '~/tools/run_pvt.pl "'$BUILD_NUMBER'"'
didn't know that bash reacts differently with single quotes and double quotes. Thanks!
linux core developers must burn in hell
@goldstar, note that the difference between single quote and double quote behaviour in the shell predates Linux by decades.
PSA: if your string contains user input, this is a very bad idea, and could open you up to code injection attacks.
|
35

Variables in single-quotes are not evaluated. Use double quotes:

ssh [email protected] "~/tools/run_pvt.pl $BUILD_NUMBER"

The shell will expand variables in double-quotes, but not in single-quotes. This will change into your desired string before being passed to the ssh command.

Comments

7

The list of accepted environment variables on SSHD by default includes LC_*. Thus:

LC_MY_BUILDN="1.2.3" ssh -o "SendEnv LC_MY_BUILDN" ssh-host 'echo $LC_MY_BUILDN'
1.2.3

1 Comment

Quoting the sshd_conf manual, "The default is not to accept any environment variables." It seems some Linux distros add AcceptEnv LANG LC_* to the default config, but I wouldn't rely on this.
6

(This answer might seem needlessly complicated, but it’s easily extensible and robust regarding whitespace and special characters, as far as I know.)

You can feed data right through the standard input of the ssh command and read that from the remote location.

In the following example,

  1. an indexed array is filled (for convenience) with the names of the variables whose values you want to retrieve on the remote side.
  2. For each of those variables, we give to ssh a null-terminated line giving the name and value of the variable.
  3. In the shh command itself, we loop through these lines to initialise the required variables.
# Initialize examples of variables.
# The first one even contains whitespace and a newline.
readonly FOO=$'apjlljs ailsi \n ajlls\t éjij'
readonly BAR=ygnàgyààynygbjrbjrb

# Make a list of what you want to pass through SSH.
# (The “unset” is just in case someone exported
# an associative array with this name.)
unset -v VAR_NAMES
readonly VAR_NAMES=(
    FOO
    BAR
)

for name in "${VAR_NAMES[@]}"
do
    printf '%s %s\0' "$name" "${!name}"
done | ssh [email protected] '
    while read -rd '"''"' name value
    do
        export "$name"="$value"
    done

    # Check
    printf "FOO = [%q]; BAR = [%q]\n" "$FOO" "$BAR"
'

Output:

FOO = [$'apjlljs ailsi \n ajlls\t éjij']; BAR = [ygnàgyààynygbjrbjrb]

If you don’t need to export those, you should be able to use declare instead of export.

A really simplified version (if you don’t need the extensibility, have a single variable to process, etc.) would look like:

$ ssh [email protected] 'read foo' <<< "$foo"

2 Comments

How would it work with an EOF series of commands?
@MattiaRasulo What do you call “an EOF series of commands”? Do you mean commands given in an “here document” (<< _FOO_ … _FOO_)? I guess you can wrap that with a "$(cat …)" to give its contents to ssh as its first positional parameter, but then why not directly write the commands in a string (like in the example from my answer, but possibly with " instead of ' if need be) rather than resorting to an “here document” at all? But I’m not sure at all that’s what you mean.
2

It is also possible to pass environment variables explicitly through ssh. It does require some server-side set-up through, so this this not a universal answer.

In my case, I wanted to pass a backup repository encryption key to a command on the backup storage server without having that key stored there, but note that any environment variable is visible in ps! The solution of passing the key on stdin would work as well, but I found it too cumbersome. In any case, here's how to pass an environment variable through ssh:

On the server, edit the sshd_config file, typically /etc/ssh/sshd_config and add an AcceptEnv directive matching the variables you want to pass. See man sshd_config. In my case, I want to pass variables to borg backup so I chose:

AcceptEnv BORG_*

Now, on the client use the -o SendEnv option to send environment variables. The following command line sets the environment variable BORG_SECRET and then flags it to be sent to the client machine (called backup). It then runs printenv there and filters the output for BORG variables:

$ BORG_SECRET=magic-happens ssh -o SendEnv=BORG_SECRET backup printenv | egrep BORG
BORG_SECRET=magic-happens

1 Comment

You can "smuggle" your variables in using default server side settings, see my answer. The gist is, that default OpenSSHd config includes LC_* as allowed Variables to send, so just use $LC_TvE_foo, or $LC_BORG_SECRET, just make sure you don't "collide" with a built-in variable.
0

As answered previously, you do not need to set the environment variable on the remote host. Instead, you can simply do the meta-expansion on the local host, and pass the value to the remote host.

ssh [email protected] '~/tools/run_pvt.pl $BUILD_NUMBER'

If you really want to set the environment variable on the remote host and use it, you can use the env program

ssh [email protected] "env BUILD_NUMBER=$BUILD_NUMBER ~/tools/run_pvt.pl \$BUILD_NUMBER"

In this case this is a bit of an overkill, and note

  • env BUILD_NUMBER=$BUILD_NUMBER does the meta expansion on the local host
  • the remote BUILD_NUMBER environment variable will be used by
    the remote shell

Comments

-1

Escape the variable in order to access variables outside of the ssh session: ssh [email protected] "~/tools/myScript.pl \$BUILD_NUMBER"

3 Comments

This does not achieve what the question is asking for.
from a shell point of view, '$FOO' is equivalent to "\$FOO". the question was "how to pass a shell variable with SSH?". As already stated by @PatrickTrentin this is not a correct answer because then BUILD_NUMBER environment variable is not set remotely.
I upvoted this one because, while it does not specifically answer the original question, it answers (at least for me) the next question that comes to mind: for remote commands where I want to interpolate variables, what about where I also want to suppress interpolation for a single variable? This is helpful if I want to interpolate a variable for looping, but then remotely interpolate the loop variable.
-1

If you need run it from a particular shell you can do:

 ssh [email protected] "bin/bash -c -i '~/tools/myScript.pl $BUILD_NUMBER'"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.