How to Set Expect Timeout (120 Seconds) for SSH Automation in Bash Scripts


3 views

When automating SSH sessions with Expect in Bash, timeout management becomes crucial for robust scripts. The default timeout (10 seconds) often proves insufficient for real-world scenarios like slow network connections or server load spikes.

The timeout variable controls how long Expect waits for patterns to match. Here's how to modify our original script with a 120-second timeout:

#!/bin/bash
HOST="localhost"
USER="myuname"
PASS="mypassword"

VAR=$(expect -c "
set timeout 120
spawn ssh $USER@$HOST
expect \"password:\"
send \"$PASS\r\"
expect \"\\\\$\"
send \"ls\r\"
expect -re \"$USER.*\"
send \"logout\"
")

echo "==============="
echo "$VAR"

For more granular control, you can set different timeouts for specific operations:

#!/bin/bash
# Connection timeout (longer for initial connection)
expect -c "
set timeout 180
spawn ssh $USER@$HOST
expect \"password:\"

# Shorter timeout for command execution
set timeout 60
send \"$PASS\r\"
expect \"\\\\$\"
send \"ls -l\r\"

# Very short timeout for prompt detection
set timeout 5
expect \"$USER.*\"
send \"exit\r\"
"

Use conditional logic to manage timeout events:

expect {
    timeout {
        send_user \"Connection timed out\n\"
        exit 1
    }
    "password:" {
        send \"$PASS\r\"
    }
}

When setting large timeout values (like 120 seconds):

  • Consider implementing a progress indicator
  • Add logging for timeout events
  • Combine with SSH's own ConnectTimeout option
spawn ssh -o ConnectTimeout=60 $USER@$HOST

When automating SSH sessions using Expect in Bash, one common pain point is handling scenarios where the remote server becomes unresponsive. Without proper timeout handling, your script could hang indefinitely, causing automation pipelines to fail.

Expect has built-in timeout functionality that can be configured at two levels:
1. Global timeout (affects all expect commands)
2. Per-command timeout (specific to individual expect patterns)

Here's how to modify your script to include both types of timeouts:

#!/bin/bash
HOST="localhost"
USER="myuname"
PASS="mypassword"

VAR=$(expect -c "
set timeout 120  # Global timeout of 120 seconds
spawn ssh $USER@$HOST
expect {
    timeout { send_user \"\\nTimeout occurred during SSH connection\\n\"; exit 1 }
    \"password:\"
}
send \"$PASS\\r\"
expect {
    timeout { send_user \"\\nTimeout waiting for prompt\\n\"; exit 1 }
    \"\\\\$\"
}
send \"ls\\r\"
expect {
    timeout { send_user \"\\nTimeout waiting for command output\\n\"; exit 1 }
    -re \"$USER.*\"
}
send \"logout\\r\"
expect eof
")

echo "==============="
echo "$VAR"

For more complex scenarios, consider these approaches:

# Nested timeout handling
expect {
    -timeout 30 "first pattern" {
        # Handle successful match
    }
    -timeout 60 "second pattern" {
        # Different timeout for this pattern
    }
    timeout {
        send_user "Custom timeout message\\n"
        exit 2
    }
}

# Dynamic timeout adjustment
set original_timeout $timeout
set timeout [expr {$original_timeout / 2}]
expect "quick response"
set timeout $original_timeout

When troubleshooting timeout problems, add diagnostic output:

expect {
    timeout {
        send_user "\\nFailed at [clock format [clock seconds]]\\n"
        send_user "Last seen buffer: $expect_out(buffer)\\n"
        exit 1
    }
    "expected pattern"
}
  • Always include timeout handling for every expect command
  • Use different timeout values based on command expectations (login vs. command execution)
  • Implement proper error logging and exit codes
  • Consider using expect's "interact" command for interactive debugging