5

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.

1
  • 1
    made it work with one-liners: CURRENT_LOG=$(ssh s1.somain.com "mysql -u root -pXXX --execute=\"SHOW MASTER STATUS\" -AN | awk '{print \$1}'") Commented Jan 15, 2014 at 18:11

5 Answers 5

6

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

ssh s1.domain.com <<\ENDSSH
    # ...
ENDSSH
Sign up to request clarification or add additional context in comments.

1 Comment

This way it works on remote, but also the $CURRENT_LOG variable becomes a remote one. How do I copy two remote vars back to the originating machine?
2

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

2 Comments

Tryed with 1..3 backslashes – false.
I know it sounds crazy, (and its been 2-3 years since I did any of this), but I know I've surprised myself that I've had to use as many as 5 '\' chars. This will not solve the problem of having the var values appear in you local environment. I think you'll have to capture and parse output from the remote on the local side. Good luck.
2

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.)

Comments

1

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

}

Comments

0
doRemote() {

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

doRemote

1 Comment

trying to accomplish that without temporary files.

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.