如何在Bash脚本中使用远程命令结果来分配本地变量?

5
我正在编写一个脚本,用于在一组服务器上恢复主从复制。在bash中,我迷失了语法,在尝试使用本地值来分配一个远程运行命令替换的结果的局部变量时:
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
}

问题出在赋值CURRENT_*变量的两行代码上:执行mysql -u...命令时会在本地而非远程会话中执行。

请指导如何远程运行该命令,并将本地变量赋值为远程mysql命令的结果。


1
使用一行命令使其工作: CURRENT_LOG=$(ssh s1.somain.com "mysql -u root -pXXX --execute=\"SHOW MASTER STATUS\" -AN | awk '{print \$1}'") - Serge
5个回答

6
尝试按照以下方式转义ENDSSH,以便在远程主机上进行变量评估:
ssh s1.domain.com <<\ENDSSH
    # ...
ENDSSH

这样可以在远程工作,但$CURRENT_LOG变量也变成了远程变量。我如何将两个远程变量复制回原始机器? - Serge

2
尝试转义您的命令替换调用。
CURRENT_LOG=`mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}'`

现在是

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

或者加入90年代的潮流,使用$(...)形式的命令代换(也需要转义)。
CURRENT_LOG=\$(mysql -u root -pXXX --execute="SHOW MASTER STATUS" -AN | awk '{print $1}' )

为了正确转义,您可能需要使用多个'\'字符。

IHTH


尝试使用1到3个反斜杠 - 错误。 - Serge
我知道听起来很疯狂(而且自从我做过这些以来已经过去了2-3年),但我知道我曾经惊讶地发现我必须使用多达5个 '' 字符。这并不能解决在本地环境中显示变量值的问题。我认为你需要在本地端捕获和解析远程输出。祝你好运。 - shellter

2

所以,你有两个完全不同的问题:

  • 在调用 ssh 之前,你正在本地运行嵌入式命令,而你打算将其作为传递给 ssh 的输入的一部分在远程上运行。
  • 你正在远程主机上分配变量,而你打算在本地主机上分配它们。

其他答案已经涵盖了 #1(即更好的转义),因此我将涵盖 #2。

#2 很棘手,但一种方法是修改 ssh 命令以打印需要在本地运行的赋值语句。然后,你可以将其包装在一个运行这些赋值语句的 eval 命令中。

总的来说,你最终会得到像这样的东西:

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)"
}

(注意我在所有先前的命令中添加了>&2,以便它们的输出流到标准错误而不是标准输出。这是因为标准输出被捕获并且本地进行了eval,这显然不是您想要的。)

1

还可以将整个文档的内容存储在一个变量中,然后将此变量作为参数传递给ssh命令。

添加一些echo语句,就可以在本地机器上访问${CURRENT_LOG}${CURRENT_POS}变量。只需确保下面的$ssh_output仅包含echo语句的输出(但也请参见:在等待ssh时运行并行命令)。

如果ssh将执行提示输入密码的sudo,请不要从此处文档重定向stdin。

# 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

}

0
doRemote() {

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

doRemote

试图在不使用临时文件的情况下完成该任务。 - Serge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接