Search code examples
bashsshexit-code

How to separate remote ssh exit codes by groups?


I'm doing remote SSH request like ssh root@server "cat somefile" .

How do I determine whether it fails because of command/file issues (e.g. file does not exist, wrong command, wrong argument, etc.) or connection issues (e.g. SSH port is closed, the host is down, DNS is not resolved, etc.)?

I know I can use specific exit codes but is there some generic way to not specify every single exit code? Something like

if [ $? -eq 1 ]; then
    echo "command error"
elif [ $? -eq 2 ]; then
    echo "connection error"
fi

Solution

  • From man ssh:

    ssh exits with the exit status of the remote command or with 255 if an error occurred.

    So there is only one special exit code. You don't have to keep a list of exit codes for things like "SSH port is closed", "the host is down", and so on.

    If your command never exits with 255 or you don't care about that specific code you can use

    ssh root@server 'some command'
    sshstatus=$?
    if (( status == 255 )); then
        echo "connection error"
    elif (( status != 0 )); then
        echo "command error"
    fi
    

    If you need to catch all exit codes (even 255) from your command, or you really, really, really, don't want to manually specify any special exit codes, then you have to print the command's exit code on the server side and extract it on the client side:

    cmdstatusfile=$(mktemp)
    ssh root@server 'some command; status=$?; wait; printf %3d $status' |
        tee >(tail -c3 "$cmdstatusfile") | head -c-3
    sshstatus=$?
    cmdstatus=$(< "$cmdstatusfile")
    rm "$cmdstatusfile"
    if (( sshstatus != 0 )); then
        echo "connection error"
    elif (( cmdstatus != 0 )); then
        echo "command error"
    fi
    

    If you normally pipe ssh ... into something you can still do so. Simply add your pipe after head ....

    This approach assumes that some command always exits normally. With set -e or exit inside some command the exit code wouldn't be printed at the end. In those cases you could print the exit code inside a trap.