You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:用临时文件传递退出码

如果担心命令输出干扰退出码捕获,可以让远程命令把退出码写入临时文件,再读取返回:

  1. 远程执行:sudo ${cmd}; echo \$? > /tmp/cmd_exit.tmp
  2. 接着执行cat /tmp/cmd_exit.tmp,在expect里捕获输出作为退出码
  3. 最后可选删除临时文件: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 01:53:20