调用函数

时间:2019-05-29 15:19:27

标签: bash shell hang

我目前正在测试bash脚本以执行数据库迁移。

这些脚本基本上接受一些参数,例如:

  • 要迁移的数据库的名称
  • 从中迁移服务器的服务器
  • 将其迁移到的服务器

在脚本中,有一个函数可以“构建”要执行的mysql和mysqldump命令,具体取决于发件人/目的地服务器是本地服务器还是远程服务器。

函数build_mysql_command的用法如下:

_query="$(build_mysql_command mysql from)"
_dump="$(build_mysql_command mysqldump from)"
_restore="$(build_mysql_command mysql to)"

但是,当函数build_mysql_command必须调用open_ssh_tunnel时,它会挂在最后一条指令上,正如我通过将脚本与-x开关一起使用所测试的那样。

相反,如果我将SSH隧道开口放在build_mysql_command外面,然后从那里删除呼叫,那么它就可以了。

但是,我不认为我在上述功能上犯了任何错误,所以我不理解为什么脚本会挂起。

下面是一个非常简单的示例,它显示了问题,其中我用1.2.3.4替换了远程服务器的实际IP地址:

#!/bin/bash
set -x
set -o pipefail

# $1 = 'from' or 'to'
get_local_port() {
    case "$1" in
        from)
            echo 30303
        ;;

        to)
            echo 31313
        ;;

        *)
            echo 0
        ;;
    esac
}

# $1 = 'from' or 'to'
build_ssh_command() {
    local _ssh="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no"

    if [ ! -z "${params[password-$1]}" ] ; then
        _ssh="sshpass -f ${params[password-$1]} $_ssh"
    fi

    echo "$_ssh"
}

# $1 = 'from' or 'to'
open_ssh_tunnel() {
    # se non già aperto
    if [ -z "${cleanup[ssh-tunnel-$1]}" ] ; then
        local _port="$(get_local_port "$1")"
        local _ssh="$(build_ssh_command "$1")"
        local _pregp="fnNTL $_port:localhost:3306 ${params[migrate-$1]}"
        local _command="$_ssh -$_pregp"

        # tento apertura tunnel SSH
        if ! $_command ; then
            return 1
        else
            # salvo PID del tunnel così aperto
            local _pid="$(pgrep -f "$_pregp" 2> /dev/null)"

            if [ -z "$_pid" ] ; then
                return 1
            fi

            cleanup["ssh-tunnel-$1"]="$_pid"
        fi
    fi

    return 0
}

# verifica se un indirizzo fa riferimento alla macchina locale
# $1 = indirizzo da verificare
is_local_address() {
    local _host="$(hostname)"

    case "$1" in
        localhost|"127.0.0.1"|"$_host")
            return 0
        ;;

        *)
            return 1
        ;;
    esac
}

# costruisce un comando di dump o restore MySQL
# $1 = comando di base
# $2 = tipo server ('from' o 'to')
build_mysql_command() {
    local _command="$1 --user=root --password=xxx"

    if is_local_address "${params[migrate-$2]}" ; then
        # connessione tramite socket
        _command="$_command --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock"
    elif open_ssh_tunnel "$2" ; then
        # altrimenti uso connessione tramite tunnel SSH
        _command="$_command --protocol=tcp --host=localhost --port=$(get_local_port "$2")"
    else
        _command=""
    fi

    echo "$_command"
}

# parametri di esecuzione dello script
declare -A params=(
    ["migrate-from"]="localhost"
    ["migrate-to"]="1.2.3.4"
)

_query="$(build_mysql_command "mysql" "from")"
echo "_query = $_query"

_dump="$(build_mysql_command "mysqldump" "to")"
echo "_dump = $_dump"

# fine test

这是运行时的输出:

+ set -o pipefail
+ params=(["migrate-from"]="localhost" ["migrate-to"]="1.2.3.4")
+ declare -A params
++ build_mysql_command mysql from
++ local '_command=mysql --user=root --password=xxx'
++ is_local_address localhost
+++ hostname
++ local _host=my.host.name
++ case "$1" in
++ return 0
++ _command='mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
++ echo 'mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
+ _query='mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
+ echo '_query = mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
_query = mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock
++ build_mysql_command mysqldump to
++ local '_command=mysqldump --user=root --password=xxx'
++ is_local_address 1.2.3.4
+++ hostname
++ local _host=asp10.626suite-online.it
++ case "$1" in
++ return 1
++ open_ssh_tunnel to
++ '[' -z '' ']'
+++ get_local_port to
+++ case "$1" in
+++ echo 31313
++ local _port=31313
+++ build_ssh_command to
+++ local '_ssh=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
+++ '[' '!' -z '' ']'
+++ echo 'ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
++ local '_ssh=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
++ local '_pregp=fnNTL 31313:localhost:3306 1.2.3.4'
++ local '_command=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -fnNTL 31313:localhost:3306 1.2.3.4'
++ ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -fnNTL 31313:localhost:3306 1.2.3.4
Warning: Permanently added '1.2.3.4' (ECDSA) to the list of known hosts.
+++ pgrep -f 'fnNTL 31313:localhost:3306 1.2.3.4'
++ local _pid=8919
++ '[' -z 8919 ']'
++ cleanup["ssh-tunnel-$1"]=8919
++ return 0
+++ get_local_port to
+++ case "$1" in
+++ echo 31313
++ _command='mysqldump --user=root --password=xxx --protocol=tcp --host=localhost --port=31313'
++ echo 'mysqldump --user=root --password=xxx --protocol=tcp --host=localhost --port=31313'

如您所见,该脚本在打开通往远程服务器的SSH隧道时挂在build_mysql_command的最后一行,但是在构建本地命令时却没有问题。

0 个答案:

没有答案