在linux 服务器上执行以下脚本 exportSqlToSftp.sh 时报错,怎么排查?


在linux 服务器上执行以下脚本 exportSqlToSftp.sh 内容时会出现报错。

#!/bin/bash
# 执行提示 expect 命令找不到,使用 yum install expect 安装。
# 以下链接信息都是测试环境,需要后期手工调整。
# TDSQL 数据库连接信息
# TDSQL_PASSWORD 密码用单引号,不用双引号 不进行转义,防止密码中有特殊字符被转义
TDSQL_HOST="数据库连接的IP"
TDSQL_PORT="数据库连接的端口"
TDSQL_USER="数据库连接的用户名"
TDSQL_PASSWORD='数据库连接的密码'
TDSQL_DATABASE="数据库连接的库"
# 多个表名,用空格分隔
TDSQL_TABLES="qw_thondinfo gs_thondinfo"

# 导出文件的本地路径(服务器上的本地目录,绝对路径)
LOCAL_DIR="/home/sql/"
TIMESTAMP=$(date +%Y%m%d%H%M%S)

# SFTP 目标服务器信息(SFTP REMOTE_DIR 目录必须是已经存在的)
SFTP_HOST="目标服务器的IP"
SFTP_PORT="目标服务器提供SFTP用户访问的端口号"
SFTP_USER="SFTP 连接的用户名"
SFTP_PASSWORD='SFTP 连接的密码'
SFTP_REMOTE_DIR="/test"

# 遍历每个表进行导出
for table in $TDSQL_TABLES; do
    LOCAL_SQL_FILE="${LOCAL_DIR}${table}_${TIMESTAMP}.sql"
	mysqlpump -h $TDSQL_HOST -P $TDSQL_PORT -u $TDSQL_USER -p"$TDSQL_PASSWORD" $TDSQL_DATABASE $table > $LOCAL_SQL_FILE
	if [ $? -eq 0 ]; then
		echo "表 $table 数据导出成功,导出文件为: $LOCAL_SQL_FILE"
	else
		echo "表 $table 数据导出失败,请检查数据库连接信息。"
		continue
	fi
	
	# 上传文件到 SFTP 服务器
	expect <<EOF
spawn sftp -P $SFTP_PORT $SFTP_USER@$SFTP_HOST
expect "password:"
send "$SFTP_PASSWORD\n"
expect "sftp>"
send "put $LOCAL_SQL_FILE $SFTP_REMOTE_DIR\n"
expect "sftp>"
send "bye\n"
expect eof
EOF
	if [ $? -eq 0 ]; then
		echo "表 $table 的导出文件上传到 SFTP 服务器成功。"
	else
		echo "表 $table 的导出文件上传到 SFTP 服务器失败,请检查 SFTP 连接信息。"
	fi
done

报错一:

[root@50-131-226-215 sql]# sh exportSqlToSftp.sh
mysqlpump: [Warning] Using a password on the command line interface can be insecure.
mysqlpump: [ERROR] 1045: Proxy ERROR: Access denied for user 'airp_iopc'@'50.131.226.215' (using password: 'YES'): username or password error when trying to connect
表 qw_thondinfo 数据导出失败,请检查数据库连接信息。
mysqlpump: [Warning] Using a password on the command line interface can be insecure.
mysqlpump: [ERROR] 1045: Proxy ERROR: Access denied for user 'airp_iopc'@'50.131.226.215' (using password: 'YES'): username or password error when trying to connect

排查思路:

根据报错信息,是执行连接数据库的命令时,密码不对。

但是将 数据库的密码单独拿出来用 命令连接可以连接成功:

连接远程MySQL服务器
mysql -h 192.168.1.100 -P 3307 -u admin -p mydatabase
  • -h 192.168.1.100: 指定远程服务器 IP 或域名
  • -P 3307: 指定非默认端口(若端口是默认的 3306,可省略 -P 3307
  • -u admin: 使用用户名 admin
  • mydatabase: 直接进入名为 mydatabase 的数据库

说明密码本身没有问题。

既然密码本身没有问题,脚本文件上的密码也对上没有问题。

那就有可能是在执行脚本文件的过程中出了问题,提示密码错误。

有可能是在linux上执行时密码不对,为啥不对?脚本文件是在windows上用notepadd++写的,是不是换到linux上执行密码变了?

考虑到“密码”的特殊性,有可能会包含特殊字符。比如 : 123]#%&$。


当 Shell 脚本中定义的密码变量包含特殊字符(如 $, #, &, ] 等)时,直接使用 $TDSQL_PASSWORD 可能会因 Shell 解析或命令行参数处理异常导致密码被截断或转义错误。

原因:Shell 特殊字符未转义

Shell 中某些字符(如 $, #, &)具有特殊含义,若未正确处理会导致密码解析错误。 示例脚本

# 错误写法
TDSQL_PASSWORD="123]#%&$"
mysql -u root -p$TDSQL_PASSWORD -h localhost  

比如以下特殊字符:

  • $ 符号会被解析为变量替换的开始(即使后面没有有效变量名)。
  • # 在 Shell 中表示注释,其后的内容会被忽略。
  • & 会导致命令在后台执行,中断参数传递。

解决方案:用双引号包裹密码变量,同时在定义密码变量的时候用单引号包裹实际密码,避免特殊字符被 Shell 解析:

正确写法:
TDSQL_PASSWORD='123]#%&$'
...
mysql -u root -p"$TDSQL_PASSWORD" -h localhost  # 正确写法

报错二:执行脚本数据库连接报错问题解决后,又出现了文件上传失败的报错:

-[root@950-131-226-215 sql]# sh exportSqlToSftp
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF.
complete dump, pass --all-databases --triggers --routines --events
exportSqlToSftp.sh:行37: expect: 未找到命令
mysql/sql tbondbond数据导出成功, 导出文件为: /home/sql/tbondbond_20250514152931.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF.
complete dump, pass --all-databases --triggers --routines --events
exportSqlToSftp.sh:行37: expect: 未找到命令
mysql/sql tbondbond数据导出成功, 导出文件为: /home/sql/tbondbond_20250514152948.sql
exportSqlToSftp.sh:行37: expect: 未找到命令
tbondbond数据导出失败, 请检查SFTP连接信息。

image-20250514172502575

以上报错是 执行提示 expect 命令找不到,使用 yum install expect 安装。

直接在服务器上执行命令:

yum install expect

根据提示,一路点 yes 就行。

报错三:解决expect 命令找不到后,又报错—将文件导出到远程服务器报错

[root@50-131-226-215 sql]# sh exportSqlToSftp.sh
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions.
complete dump, pass --all-databases --triggers --routines --events.
表 qy_tbondinfo 数据导出成功, 导出文件为: /home/sql/qy_tbondinfo_20250514153157.sql
spawn sftp -P 10022 airp_lc_sftp@50.131.232.114
ssh: connect to host 50.131.232.114 port 10022: Connection refused
Connection closed.
Connection closed
send: spawn id exp4 not open
    while executing
'send "Qw@3124!\n"'
表 qy_tbondinfo 的导出文件上传到 SFTP 服务器失败, 请检查 SFTP 连接信息。
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions.
complete dump, pass --all-databases --triggers --routines --events.
表 gs_tbondinfo 数据导出成功, 导出文件为: /home/sql/gs_tbondinfo_20250514153157.sql
spawn sftp -P 10022 airp_lc_sftp@50.131.232.114
ssh: connect to host 50.131.232.114 port 10022: Connection refused
Connection closed
Connection closed.
send: spawn id exp4 not open
    while executing
'send "Qw@3124!\n"'
表 gs_tbondinfo 的导出文件上传到 SFTP 服务器失败, 请检查 SFTP 连接信息。
[root@50-131-226-215 sql]#

将脚本文件中的 sftp 命令单独拿出来执行发现报错,命令如下:

sftp -P 远程服务器的端口号 sftp的用户名@sftp用户连接的远程服务器IP

由于之前直接用 sftp sftp用户名@远程ip命令成功过,所以排除是sftp的用户名 和 密码的问题。

对比两个命令的差异,报错的命令因为多了端口号,所以有可能是 该远程服务器该端口没有开放访问权限或者防火墙拦截了。

又搜了下 sftp sftp用户名@远程ip不传端口号会使用默认的端口号连接,所以会成功。


SFTP 命令的技巧

sftp root@ipsftp -P port root@ip 都是用于通过 SSH 文件传输协议(SFTP)连接到远程服务器的命令,二者主要区别如下:

连接端口

  • sftp root@ip:不指定端口时,sftp 命令默认使用 SSH 的标准端口 22 来建立连接。如果远程服务器的 SSH 服务运行在标准端口上,该命令可正常连接 。
  • sftp -P port root@ip-P 选项用于显式指定连接远程服务器的端口号,port 是具体端口数值。当远程服务器的 SSH 服务运行在非标准端口(不是 22 端口 )时,就需要使用该形式指定正确端口才能成功连接。例如 sftp -P 2222 root@192.168.1.100 ,表示连接 192.168.1.100 这台服务器的 2222 端口。

使用场景

  • sftp root@ip:适用于远程服务器使用默认 22 端口运行 SSH 服务的常规场景,使用相对简洁。
  • sftp -P port root@ip:适用于远程服务器修改了默认 SSH 端口的场景。为增强安全性,很多管理员会将 SSH 服务端口修改为其他端口,此时就必须用 -P 选项指定实际端口才能连接。

DeepSeek 优化脚本,将原来导出成sql文件,改成导出生成 .gz 格式的文件

#!/bin/bash
set -o pipefail  # 确保管道中任意命令失败时整个管道返回非零状态

# 检查 expect 是否安装
if ! command -v expect &> /dev/null; then
    echo "检测到 expect 未安装,正在自动安装..."
    yum install -y expect
fi

# TDSQL 数据库连接信息
TDSQL_HOST="数据库连接的IP"
TDSQL_PORT="数据库连接的端口"
TDSQL_USER="数据库连接的用户名"
TDSQL_PASSWORD='数据库连接的密码'  # 保持单引号避免特殊字符转义
TDSQL_DATABASE="数据库连接的库"
TDSQL_TABLES="qw_thondinfo gs_thondinfo"  # 表名用空格分隔

# 导出文件的本地路径(绝对路径)
LOCAL_DIR="/home/sql/"
TIMESTAMP=$(date +%Y%m%d%H%M%S)

# SFTP 服务器信息
SFTP_HOST="目标服务器的IP"
SFTP_PORT="目标服务器的SFTP端口"
SFTP_USER="SFTP用户名"
SFTP_PASSWORD='SFTP密码'
SFTP_REMOTE_DIR="/test"

# 确保本地目录存在
mkdir -p "$LOCAL_DIR"

# 遍历每个表进行导出和压缩
for table in $TDSQL_TABLES; do
    LOCAL_GZ_FILE="${LOCAL_DIR}${table}_${TIMESTAMP}.sql.gz"
    
    # 使用 mysqlpump 导出并直接压缩为 .gz
    mysqlpump -h "$TDSQL_HOST" -P "$TDSQL_PORT" -u "$TDSQL_USER" -p"$TDSQL_PASSWORD" \
        "$TDSQL_DATABASE" "$table" | gzip > "$LOCAL_GZ_FILE"
    
    if [ $? -eq 0 ]; then
        echo "表 $table 数据导出并压缩成功,文件: $LOCAL_GZ_FILE"
    else
        echo "表 $table 导出或压缩失败,请检查数据库连接或权限。"
        rm -f "$LOCAL_GZ_FILE"  # 删除可能生成的空文件
        continue
    fi

    # 上传 .gz 文件到 SFTP
    expect <<EOF
spawn sftp -P "$SFTP_PORT" "$SFTP_USER@$SFTP_HOST"
expect "password:"
send "$SFTP_PASSWORD\r"
expect "sftp>"
send "put $LOCAL_GZ_FILE $SFTP_REMOTE_DIR\r"
expect "sftp>"
send "bye\r"
expect eof
EOF

    if [ $? -eq 0 ]; then
        echo "文件 $LOCAL_GZ_FILE 上传成功。"
    else
        echo "文件 $LOCAL_GZ_FILE 上传失败,请检查 SFTP 配置。"
    fi
done
JAVA-技能点
知识点