mysql 客户端在使用脚本运行时打印帮助消息,但直接在 shell 中运行良好

问题描述

我遇到了一个奇怪的 shell 脚本问题。我正在尝试从 shell 针对本地 docker 实例或通过 ssh 针对不可公开访问的数据库运行 MysqL 命令。我有这个:

#!/bin/bash

# 0: Check DB status
# 1: Query MysqL for latest migration
# 2: Check sql dir for migrations newer than query result
# 3..: Execute newer sql *in order*
 
################################################################################
# script and sql calls
################################################################################

DB_HOST=$1
DB_USER=$2
DB_PASSWORD=$3
DB_NAME=$4

if [[ -z $REMOTE_HOST ]]; then
    SSH_PREFIX=""
else
    SSH_PREFIX="ssh -o StrictHostKeyChecking=no -i sshkey ${REMOTE_USER}@${REMOTE_HOST}"
fi

MysqL_CLIENT="MysqL --protocol=tcp -h ${DB_HOST} -u ${DB_USER} --password=${DB_PASSWORD}"

#checking MysqL connection
MysqL_db_statuscheck(){
    echo "`date` :Checking DB connectivity...";
    echo "`date` :Trying to connect to the ODU MysqL Database..."
    EXEC_sql="-e 'SELECT 1;'"
    cmd="${SSH_PREFIX} ${MysqL_CLIENT} ${EXEC_sql} ${DB_NAME}"
    echo $cmd
    $cmd
    if [[ $? -eq 0 ]]
    then
        DB_STATUS="UP"
        export DB_STATUS
        echo "`date` :Status: ${DB_STATUS}. Able to Connect..."
    else
        DB_STATUS="DOWN"
        export DB_STATUS
        echo "`date` :Status: DOWN . Not able to Connect."
        echo "`date`:Not able to connect to database with Username:
        "${DB_USER}" HostName: ""${DB_HOST}" " SID: "${DB_NAME}"."
        echo "`date` :Exiting Script Run..."
        exit 1
    fi
}

# run MysqL function
runMysqLs() {
    echo "`date` :Checking DB and table status..."

    MysqL_db_statuscheck

    echo "`date` :DB status check completed"
    echo "`date` :Connecting To ${DB_USER}/******@${DB_NAME}";
    if [[ $DB_STATUS == "UP" ]]
    then
        # latest_migration will be an int
        EXEC_sql='--execute="SELECT MAX(version) FROM migrations;"'
        latest_migration=`$SSH_PREFIX $MysqL_CLIENT ${EXEC_sql} ${DB_NAME} 2>&1`
        if [[ `echo "${latest_migration}" |cut -c 1-10` == "ERROR 1146" ]]; then
            echo "`date` :Running initial migration"
            latest_migration=0
        fi
        
        for file in `ls ./sql`; do
            file_migration_no=`echo "$file" |cut -c1-3`
            if [[ $latest_migration -lt $file_migration_no ]]; then
                echo "`date` :Executing migration $file_migration_no from file $file...";
                echo "`date` :__________________________________________";
                echo "`date` :sql OUTPUT:";
                echo "`date` :__________________________________________";
                TAIL="-se '`cat ./sql/${file}`'"
                sqlout=`$SSH_PREFIX $MysqL_CLIENT ${TAIL} 2>&1`
                if [[ $? -eq 0 ]]; then 
                    TAIL="-se 'INSERT INTO migrations (version) VALUES (${file_migration_no});'"
                    `$SSH_PREFIX $MysqL_CLIENT ${TAIL}`
                else
                    echo ${sqlout}
                    exit 1
                fi
            fi
        done
    else
        echo "`date` :Either the DB is down or the exit status returned by
        the script shows ERROR."
        echo "`date` :Exiting ..."
        exit
    fi
}

# main function
Main() {
    echo "`date` :Starting sql auto run script."
    runMysqLs
    echo "`date` :sql auto run script execution completed."
}
Main

当我对远程数据库运行它时,它运行良好。但是当我针对本地实例运行时,它只是打印出 MysqL 帮助消息。

我正在打印要运行的命令,$ MysqL --protocol=tcp -h localhost -u root --password=hotdog99 -e 'SELECT 1;' cooldb

但是如果我将脚本输出中的内容复制并粘贴到终端中,它运行良好!

如果我设置

EXEC_sql=""

它让我进入 MysqL shell,但只要插入 -e 'SELECT 1;'--execute='SELECT 1;',它就会返回打印使用/帮助消息。

解决方法

当您针对远程数据库运行它时,您将把 'SELECT 1;' 传递给 ssh,后者将它作为命令行参数传递给 shell,而该 shell 会去掉单引号 ( '') 符合预期。

当您在本地运行它时,您只是在取消引用一个变量。您没有将它作为参数传递给 shell,因此它会按原样扩展,这意味着您传递给 mysql 的参数是 'SELECT 1;',即带引号。

您应该改用 sh -c "$cmd"

演示:

$ ARG="'hello'"
$ cmd="echo ${ARG}"
$ echo $cmd
echo 'hello'
$ $cmd
'hello'
$ sh -c "$cmd"
hello
$ cmd="ssh 0 echo ${ARG}"
$ $cmd
hello