Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is it possible to use variables in remote ssh command?

I'd like to execute several commands in sequence on a remote machine, and some of the later commands depend on earlier ones. In the simplest possible example I get this:

ssh my_server "echo this is my_server; abc=2;"
this is my_server
abc=2: Command not found.
ssh my_server "echo this is my_server; abc=2; echo abc is $abc"
abc: undefined variable

For a bit of background info, what I actually want to do is piece together a path and launch a java application:

ssh my_server 'nohup sh -c "( ( echo this is my_server; jabref_exe=`which jabref`; jabref_dir=`dirname $jabref_exe`; java -jar $jabref_dir/../jabref.jar` $1 &/dev/null ) & )"' &
jabref_dir: Undefined variable.

That way, whenever jabref gets updated to a new version on the server, I won't have to manually update the path to the jar file. The jabref executable doesn't take arguments, but launching it with java -jar does, which is why I have to juggle the path a bit.

At the moment I have the list of commands in a separate script file and call

ssh my_server 'nohup sh -c "( ( my_script.sh &/dev/null ) & )"' &

which works, but since the ssh call is already inside one script file it would be nice to have everything together.

like image 742
craq Avatar asked Mar 06 '13 12:03

craq


2 Answers

In this example

ssh my_server "echo this is my_server; abc=2;"

abc is set on the remote side, so it should be clear why it is not set on your local machine.

In the next example,

ssh my_server "echo this is my_server; abc=2; echo abc is $abc"

your local shell tries to expand $abc in the argument before it is ever sent to the remote host. A slight modification would work as you expected:

ssh my_server 'echo this is my_server; abc=2; echo abc is $abc'

The single quotes prevent your local shell from trying to expand $abc, and so the literal text makes it to the remote host.


To finally address your real question, try this:

jabref_dir=$( ssh my_server 'jabref_exe=$(which jabref); jabref_dir=$(dirname $jabref_exe);
               java -jar $jabref_dir/../jabref.jar > /dev/null; echo $jabref_dir' )

This will run the quoted string as a command on your remote server, and output exactly one string: $jabref_dir. That string is captured and stored in a variable on your local host.

like image 185
chepner Avatar answered Oct 16 '22 20:10

chepner


With some inspiration from chepner, I now have a solution that works, but only when called from a bash shell or bash script. It doesn't work from tcsh.

ssh my_server "bash -c 'echo this is \$HOSTNAME; abc=2; echo abc is \$abc;'"

Based on this, the code below is a local script which runs jabref on a remote server (although with X-forwarding by default and passwordless authentication the user can't tell it's remote):

#!/bin/bash
if [ -f "$1" ]
then
    fname_start=$(echo ${1:0:4})
    if [ "$fname_start" = "/tmp" ]
    then
        scp $1 my_server:$1
        ssh my_server "bash -c 'source load_module jdk; source load_module jabref; java_exe=\$(which java); jabref_exe=\$(which jabref); jabref_dir=\$(echo \${jabref_exe%/bin/jabref});eval \$(java -jar \$jabref_dir/jabref.jar $1)'" &
    else
        echo input argument must be a file in /tmp.
else
    echo this function requires 1 argument
fi

and this is the 1-line script load_module, since modulecmd sets environment variables and I couldn't figure out how to do that without sourcing a script.

eval `/path/to/modulecmd bash load $1`;

I also looked at heredocs, inspired by How to use SSH to run a shell script on a remote machine? and http://tldp.org/LDP/abs/html/here-docs.html. The nice part is that it works even from tcsh. I got this working from the command line, but not inside a script. That's probably easy enough to fix, but I've got a solution now so I'm happy :-)

ssh my_server 'bash -s' << EOF
echo this is \$HOSTNAME; abc=2; echo abc is \$abc;
EOF
like image 40
craq Avatar answered Oct 16 '22 18:10

craq