调用函数时Bash脚本挂起
我目前正在测试一个bash脚本来执行数据库迁移 脚本基本上接受一些参数,例如:调用函数时Bash脚本挂起,bash,shell,freeze,Bash,Shell,Freeze,我目前正在测试一个bash脚本来执行数据库迁移 脚本基本上接受一些参数,例如: 要迁移的数据库的名称 从中进行迁移的服务器 将其迁移到的服务器 脚本中有一个函数,它“构建”要执行的mysql和mysqldump命令,具体取决于from/to服务器是否为本地/远程服务器 然后使用函数build\u mysql\u命令,如下所示: _query="$(build_mysql_command mysql from)" _dump="$(build_mysql_command mysqldump f
- 要迁移的数据库的名称
- 从中进行迁移的服务器
- 将其迁移到的服务器
build\u mysql\u命令
,如下所示:
_query="$(build_mysql_command mysql from)"
_dump="$(build_mysql_command mysqldump from)"
_restore="$(build_mysql_command mysql to)"
但是,当函数build\u mysql\u命令
必须调用open\u ssh\u tunnel
时,它将挂起最后一条指令,正如我使用带有-x
开关的脚本所测试的那样
相反,如果我将SSH隧道的开口放在build\u mysql\u命令
之外,并从那里删除调用,它就会工作
但是,我不认为我在上述函数中犯了任何错误,因此我不理解为什么脚本会挂起
下面是一个非常简单的示例,显示了问题所在,我将远程服务器的实际IP地址替换为1.2.3.4
:
#!/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_命令的最后一行,但在生成本地命令时没有显示问题。与您的问题无关,但不要将命令作为字符串生成;它天生就容易失败,最终你会达到极限。请看……谈到眼前的问题本身,运行ssh命令会阻塞,直到该命令退出(如果它在到达该点之前没有阻塞读取stdin),这并不奇怪。令人惊讶的是,我们希望看到这个命令实际上是什么,以及完整的参数列表;提供集合-x
跟踪的尾部是一个开始。函数中的最后一条指令是返回0
,它是挂起的吗?我同意,查看跟踪会有帮助。我根据您的评论添加了更多详细信息。@Sheller我添加了一个脚本的精简版本,该版本仍然显示问题,其输出与您的问题无关,但不要将命令构建为字符串;它天生就容易失败,最终你会达到极限。请看……谈到眼前的问题本身,运行ssh命令会阻塞,直到该命令退出(如果它在到达该点之前没有阻塞读取stdin),这并不奇怪。令人惊讶的是,我们希望看到这个命令实际上是什么,以及完整的参数列表;提供集合-x
跟踪的尾部是一个开始。函数中的最后一条指令是返回0
,它是挂起的吗?我同意,查看跟踪会有帮助。我根据您的评论添加了更多详细信息。@Shelleter我添加了一个脚本的精简版本,该版本仍然显示问题,并使用set+x