Search code examples
foreachexpectspawn

Expect getting stuck when inside a foreach after first iteration


Output is stuck after the first iteration. Works fine when only one expect exists within the loop. Using exp_continue as well fails when used.

#!/usr/bin/expect -f

exp_internal 1
set timeout -1
set passwords [list foo bar test]
set connected false
set passwordUsed "test"
spawn ssh -oHostKeyAlgorithms=+ssh-dss [email protected] -y

foreach i $passwords {
    expect "assword:" { send -- "$i\r";}
    expect "asd" {send "test"}
}  
expect eof

Output:

spawn ssh -oHostKeyAlgorithms=+ssh-dss [email protected] -y



[email protected]'s password: 



[email protected]'s password: 

debug

parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {1937}

expect: does "" (spawn_id exp4) match glob pattern "assword:"? no

expect: does "\r" (spawn_id exp4) match glob pattern "assword:"? no

expect: does "\[email protected]'s password: " (spawn_id exp4) match glob pattern "assword:"? yes
expect: set expect_out(0,string) "assword:"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "\[email protected]'s password:"
send: sending "foo\r" to { exp4 }

expect: does " " (spawn_id exp4) match glob pattern "asd"? no

expect: does " \r\n" (spawn_id exp4) match glob pattern "asd"? no

expect: does " \r\n\[email protected]'s password: " (spawn_id exp4) match glob pattern "asd"? no

Then hangs on the last expect.


Solution

  • The timeout value is the problem: expect is waiting forever to see "asd"

    You need something like this (untested)

    set passwords {foo bar baz}
    set idx 0
    spawn ...
    expect {
        "assword:" {
            if {$idx == [llength $passwords]} {
                error "none of the passwords succeeded"
            }
            send "[lindex $passwords $idx]\r"
            incr idx
            exp_continue
        }
        "asd" {send "test"}
    }
    expect eof
    

    exp_continue loops within the expect command to wait for "asd" or for "assword" to appear again.

    The "asd" case does not use exp_continue, so after sending "test", this expect commend ends.