SSH+Sudo场景下如何获取执行命令的退出码?
问题:通过SSH+Sudo执行命令时如何获取真实退出码
我现在通过SSH+Sudo执行命令,已经能判断命令是否执行失败,但没办法获取命令的真实退出码,求解决思路?
用户提供的现有代码:
# ---------------------------------------------------- # 连接至user@server并以root身份通过sudo执行命令 # 使用方法: f_ssh_sudo "server" "username" "password" "my_script.sh arg1 arg2" function f_ssh_sudo() { local server=$1 local username=$2 local pw=$3 local cmd=$4 expect -f - <<-EOF set timeout 10 spawn ssh -o StrictHostKeyChecking=no ${username}@${server} expect "*?assword:" { send -- "${pw}\n" } expect "*\$*" send -- "sudo ${cmd} && echo CMD_OK || echo CMD_FAIL\n" expect "*${username}*" { send -- "${pw}\r" } expect { "*CMD_OK*" { send -- "exit\n" ; exit 0 } "*CMD_FAIL*" { send -- "exit\n" ; exit 1 } } EOF return $? } f_ssh_sudo 192.168.1.50 gandalf "Password1234" "ls -Alph /root" echo "retCode=$?"
解决思路
思路1:直接捕获远程命令的退出码并返回
修改发送给远程的命令,让它在执行完sudo命令后直接输出带标识的退出码,然后在expect里捕获这个值并传递回来。修改后的代码如下:
# ---------------------------------------------------- # 连接至user@server并以root身份通过sudo执行命令 # 使用方法: f_ssh_sudo "server" "username" "password" "my_script.sh arg1 arg2" function f_ssh_sudo() { local server=$1 local username=$2 local pw=$3 local cmd=$4 local remote_exit_code # 捕获expect脚本的返回值,即远程命令的真实退出码 remote_exit_code=$(expect -f - <<-EOF set timeout 10 spawn ssh -o StrictHostKeyChecking=no ${username}@${server} expect "*?assword:" { send -- "${pw}\n" } expect "*\$*" # 执行sudo命令后,输出带标识的退出码 send -- "sudo ${cmd}; echo __EXIT_CODE__:\$?\n" # 处理sudo的密码提示 expect "*${username}*" { send -- "${pw}\r" } # 匹配退出码行,提取数字部分 expect { "__EXIT_CODE__:*" { set code [string range \$expect_out(buffer) [expr {[string first "__EXIT_CODE__:" \$expect_out(buffer)] + 14}] end] send -- "exit\n" # 以提取到的退出码退出expect exit [string trim \$code] } timeout { send -- "exit\n" exit 255 } } EOF ) return $remote_exit_code } f_ssh_sudo 192.168.1.50 gandalf "Password1234" "ls -Alph /root" echo "retCode=$?"
思路2:用临时文件传递退出码
如果担心命令输出干扰退出码捕获,可以让远程命令把退出码写入临时文件,再读取返回:
- 远程执行:
sudo ${cmd}; echo \$? > /tmp/cmd_exit.tmp - 接着执行
cat /tmp/cmd_exit.tmp,在expect里捕获输出作为退出码 - 最后可选删除临时文件:
rm -f /tmp/cmd_exit.tmp
思路3:改用免密方式(推荐)
硬编码密码风险高,建议用更安全的方案:
- 将本地用户SSH公钥复制到远程服务器的
~/.ssh/authorized_keys,实现SSH免密登录 - 在远程
sudoers文件中配置该用户无需密码执行指定命令(比如gandalf ALL=(ALL) NOPASSWD: ALL,或限制特定命令)
之后直接用ssh ${username}@${server} "sudo ${cmd}"就能直接获取真实退出码,无需expect处理密码,代码更简洁可靠。
内容的提问来源于stack exchange,提问作者LoB
相关产品推荐
相关产品推荐

