Reputation: 13334
I am running scripts on a remote server from a local server via SSH. The script gets copied over using SCP in a first place, then called while being passed some arguments as follows:
scp /path/to/script server.example.org:/another/path/
ssh server.example.org \
MYVAR1=1 \
MYVAR2=2 \
/another/path/script
This works fine and on the remote server, the variables MYVAR1
and MYVAR2
are available with their corresponding value.
The issue is that these scripts are in constant development which requires the SSH command to be changed every-time a variable is renamed, added, or removed.
I'm looking for a way of passing all the local environment variables to the remote script (since MYVAR1
and MYVAR2
are actually local environment variables) which would address the SSH command maintenance issue.
Since MYVAR1=1 \
and MYVAR1=1 \
are lines which follow the env
command output I tried replacing them with the actual command as follows:
ssh server.example.org \
`env`
/another/path/script
This seems to work for "simple" env
output lines (e.g. SHELL=/bin/bash
or LOGNAME=sysadmin
), however I get errors for more "complex" output lines (e.g. LS_COLORS=rs=0:di=01;34:ln=01;[...]
which gives errors such as -bash: 34:ln=01: command not found
). I can get rid of these errors by unsetting the variables corresponding to those complex output lines before running the SSH command (e.g. unset LS_COLORS
, then ssh [...]
) however I don't find this very solution very reliable.
Q: Does anybody know how to pass all the local environment variables to a remote script via SSH?
PS: the local environment variables are not environment variables available on the remote machine so I cannot use this solution.
Update with solution
I ended using sed
to format the env
command output from VAR=VALUE
to VAR="VALUE"
(and concatenating all lines in to 1) which prevents bash from interpreting some of the output as commands and fixes my problem.
ssh server.example.org \
`env | sed 's/\([^=]*\)=\(.*\)/\1="\2"/' | tr '\n' ' '` \
"/another/path/script"
Upvotes: 6
Views: 13312
Reputation: 31
This solution works well for me. Suppose you have script which takes two params or have two variables:
#!/bin/sh
echo "local"
echo "$1"
echo "$2"
/usr/bin/ssh [email protected] "/path/test.sh \"$1\" \"$2\";"
And script test.sh on 192.168.1.2:
#!/bin/bash
echo "remote"
echo "$1"
echo "$2"
Output will be:
local
This is first params
And this is second
remote
This is first params
And this is second
Upvotes: 0
Reputation: 10943
You should use set
instead of env
.
From the bash manual:
Without options, the name and value of each shell variable are displayed in a format that can be reused as input for setting or resetting the currently-set variables.
This will take care of all your semi-colon and backslash issues.
scp /path/to/script server.example.org:/another/path/
set > environment
scp environment server.example.org:/another/path/
ssh server.example.org "source environment; /another/path/script"
If there are any variables you don't want to send over you can filter them out with something like:
set | grep -v "DONT_NEED" > environment
You could also update the ~/.bash_profile
on the remote system to run the environment script as you log in so you wouldn't have to run the environment script explicit:
ssh server.example.org "/another/path/script"
Upvotes: 3
Reputation: 10244
Perl to the rescue:
#!/usr/bin/perl
use strict;
use warnings;
use Net::OpenSSH;
use Getopt::Long;
my $usage = "Usage:\n $0 --env=FOO --env=BAR ... [user\@]host command args\n\n";
my @envs;
GetOptions("env=s" => \@envs)
or die $usage;
my $host = shift @ARGV;
die $usage unless defined $host and @ARGV;
my $ssh = Net::OpenSSH->new($host);
$ssh->error and die "Unable to connect to remote host: " . $ssh->error;
my @cmds;
for my $env (@envs) {
next unless defined $ENV{$env};
push @cmds, "export " . $ssh->shell_quote($env) .'='.$ssh->shell_quote($ENV{$env})
}
my $cmd = join('&&', @cmds, '('. join(' ', @ARGV) .')');
warn "remote command: $cmd\n";
$ssh->system($cmd);
And it will not break in case your environment variables contain funny things as quotes.
Upvotes: 1
Reputation: 9474
The problem is that ;
mark the end of your command. You must escape them:
Try whit this command:
env | sed 's/;/\\;/g'
Update: I tested the command whit a remote host and it worked for me using this command:
var1='something;using semicolons;'
ssh hostname "`env | sed 's/;/\\\\;/g' | sed 's/.*/set &\;/g'` echo \"$var1\""
I double escape ;
whit \\\\;
and then I use an other sed substitution to output variables in the form of set name=value;
. Doing this ensure every variables get setted correclty on the remote host before executing the command.
Upvotes: 2
Reputation: 12583
I happened to read the sshd_config
man page unrelated to this and found the option AcceptEnv
:
AcceptEnv Specifies what environment variables sent by the client will be copied into the session's environ(7). See SendEnv in ssh_config(5) for how to configure the client. Note that envi- ronment passing is only supported for protocol 2. Variables are specified by name, which may contain the wildcard characters
*' and
?'. Multiple environment variables may be separated by whitespace or spread across multiple AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables.
Maybe you could use this with AcceptEnv: *
? I haven't got a box with sshd handy, but try it out!
Upvotes: 7
Reputation: 12583
How about uploading the environment at the same time?
scp /path/to/script server.example.org:/another/path/
env > environment
scp environment server.example.org:/another/path
ssh server.example.org "source environment; /another/path/script"
Upvotes: 1