SSH执行远程脚本不返回exit code,如何捕获返回码做错误处理?
问题原因分析
你当前的写法本身支持捕获SSH命令的退出码,出现echo $?无返回的常见原因有2个:
- 你在执行
echo $?前运行了其他命令,覆盖了$?存储的上一条命令状态 - 远程脚本中间步骤执行失败,但最后一条命令执行成功,SSH默认返回最后一条命令的退出码0,导致你误以为没有捕获到返回值
修复方案
1. 优化远程脚本的错误传递
远程执行的脚本默认只会返回最后一条命令的退出码,中间步骤失败不会终止执行也不会透传错误,需要在远程脚本开头加执行约束:
set -e:任意命令执行非0退出时直接终止脚本set -u:遇到未定义变量直接报错退出set -o pipefail:管道命令中任意子命令失败,整个管道返回失败
同时建议给here-doc的EOF标记加单引号,避免本地变量提前展开,不需要再手动转义远程侧的变量引用。
2. 优化SSH调用参数
给SSH命令加连接控制参数,避免连接异常卡住导致状态码异常:
-o BatchMode=yes:禁用交互式密码输入,避免脚本卡住-o ConnectTimeout=10:设置10秒连接超时,避免长时间挂起
修正后的完整代码示例
#!/bin/ksh -x # 本地变量提前定义好,避免未定义 RFTPUSERID="你的用户名" RFTPSERVER="远程地址" ARCHIVEPATH="远程归档路径" ARCHIVERETENTIONDAYS="保留天数" WLDCRD="文件名通配符" LOGFILE="本地日志路径" # SSH调用,通过-s参数将本地变量传递给远程bash,EOF加单引号避免本地展开 ssh -o BatchMode=yes -o ConnectTimeout=10 "$RFTPUSERID@$RFTPSERVER" /bin/bash -s -- "${ARCHIVEPATH}" "${ARCHIVERETENTIONDAYS}" "${WLDCRD}" 2>> "${LOGFILE}" << 'EOF' # 接收传过来的参数 ARCHIVEPATH=$1 ARCHIVERETENTIONDAYS=$2 WLDCRD=$3 # 远程脚本开头加错误控制 set -euo pipefail shopt -s extglob # cd失败直接退出,不会执行后面的危险操作 cd "${ARCHIVEPATH}" find "${ARCHIVEPATH}" -type f -mtime "+${ARCHIVERETENTIONDAYS}" -name "${WLDCRD}*" -exec rm -rf {} \; date="$(date '+%Y%m%d_%H%M%S')" for file in "${ARCHIVEPATH}"!(*.gz) do newFile="${file}${date}" echo "${newFile}"; mv "${file}" "${newFile}"; gzip "${newFile}"; done EOF # 直接捕获SSH的退出码 ssh_exit_code=$? if [ ${ssh_exit_code} -eq 0 ]; then echo "远程清理执行成功" elif [ ${ssh_exit_code} -eq 255 ]; then echo "SSH连接远程节点失败" else echo "远程清理脚本执行失败,错误码:${ssh_exit_code}" fi
更优的错误处理方案
- 区分SSH连接错误和远程命令错误:SSH返回255代表连接本身失败(网络不通、权限不足、地址不存在等),1-254代表远程脚本执行返回的错误码,可以根据不同的返回码做不同的告警逻辑
- 可以将远程脚本的标准输出也写入本地日志,方便后续排查执行问题
- 对rm、mv这类高危操作,执行前可以加路径校验,避免变量异常导致误删文件
内容的提问来源于stack exchange,提问作者Sharad R. Telkar
相关产品推荐
相关产品推荐

