Serge
Serge

Reputation: 1601

How to assign local variable with a remote command result in bash script?

I'm writing a script to restore Master-Slave replication on a set of servers. Lost in bash syntax trying to assign a local variable with a result of a remotely ran command substitution with local values:

function doRemote() {
    ssh s1.domain.com   <<ENDSSH
        mysql -u root -pXXX --execute="DROP DATABASE db; CREATE DATABASE db;"
        mysql -u root -pXXX --database=db < $WORKDIR$FILENAME
        sudo rm -rf /var/log/mysql/db-bin.*
        mysql -u root -pXXX --execute="FLUSH LOGS;"
        CURRENT_LOG=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'`
        CURRENT_POS=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $2}'`
        # ...
ENDSSH
}

The two lines assigning CURRENT_* variables are the problem: the mysql -u... command gets executed locally, instead of remote session.

Please advice how to run that remotely, assigning the local variable with a result of a remote mysql command.

Upvotes: 5

Views: 5310

Answers (5)

chan
chan

Reputation: 11

It is also possible to store the contents of the entire here document in a variable and then pass this variable as an argument to the ssh command.

Adding some echo statements makes it possible to access the ${CURRENT_LOG} and ${CURRENT_POS} variables on the local machine. Just make sure $ssh_output below only contains the output of the echo statements (but also see: Run a parallel command while waiting for ssh).

Do not redirect stdin from a here document if ssh is going to execute sudo prompting for a password.

# untested
function doRemote() {

cmds="$(cat <<'ENDSSH'
mysql -u root -pXXX --execute="DROP DATABASE db; CREATE DATABASE db;"
mysql -u root -pXXX --database=db < $WORKDIR$FILENAME
sudo rm -rf /var/log/mysql/db-bin.*
mysql -u root -pXXX --execute="FLUSH LOGS;"
CURRENT_LOG=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'`
echo "export CURRENT_LOG='${CURRENT_LOG}';"
CURRENT_POS=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $2}'`
echo "export CURRENT_POS='${CURRENT_POS}';"
# ...
ENDSSH
)"

# use ssh -t for sudo command
ssh_output="$(ssh -t s1.domain.com "$cmds")"
eval "$ssh_output"
return 0

}

Upvotes: 1

ruakh
ruakh

Reputation: 183251

So, you have two completely distinct problems:

  • you are running embedded commands locally, before invoking ssh, that you intended to be running remotely, as part of the input passed over ssh.
  • you are assigning variables on the remote host that you intended to assign on the local host.

Other answers have already covered #1 (namely: better escaping), so I'll cover number #2.

#2 is tricky, but one approach is to modify the ssh command to print the assignment statements that need to run locally. You can then wrap it in an eval command that runs those assignment statements.

All told, you end up with something like this:

function doRemote() {
    eval "$(ssh s1.domain.com <<'    ENDSSH'
        mysql -u root -pXXX --execute="DROP DATABASE db; CREATE DATABASE db;" >&2
        mysql -u root -pXXX --database=db < $WORKDIR$FILENAME >&2
        sudo rm -rf /var/log/mysql/db-bin.* >&2
        mysql -u root -pXXX --execute="FLUSH LOGS;" >&2
        printf 'CURRENT_LOG=%q\n' `mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'`
        printf 'CURRENT_POS=%q\n' `mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $2}'`
        # ...
    ENDSSH)"
}

(Note that I added >&2 to all the previous commands, so their output goes to standard-error instead of standard-output. This is because standard-output gets captured and eval'd locally, which obviously you wouldn't want.)

Upvotes: 2

Chad Prey
Chad Prey

Reputation: 147

doRemote() {

ssh localhost <<ENDSSH
        ls
        cat /etc/issue
        VAR1=\$(/home/thumper/bash/test2.sh)
        echo \$VAR1 >> /home/thumper/bash/test.txt 
ENDSSH
}

doRemote

Upvotes: 0

shellter
shellter

Reputation: 37258

Try escaping your calls for cmd-substitution

CURRENT_LOG=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'`

now is

CURRENT_LOG=\`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'\`

or join the 90's ;-) and use the $( ... ) form of cmd substitution (escaped also)

CURRENT_LOG=\$(mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}' )

You might have to use more than one '\' char to get proper escaping.

IHTH

Upvotes: 2

dogbane
dogbane

Reputation: 274532

Try escaping ENDSSH as shown below, so that variable evaluation occurs on the remote host:

ssh s1.domain.com <<\ENDSSH
    # ...
ENDSSH

Upvotes: 6

Related Questions