How does Expect script determine contents of expect_out

Viewed 350

I am trying to write a script will provide credentials to a vpn with one caveat where VPN requires that I provide OTP.

This is my script:

#! /usr/bin/expect

set vpn    [ lindex $argv 0]
set group  [ lindex $argv 1]
set user   [ lindex $argv 2]
set secret [ lindex $argv 3]

log_user 2 
spawn nmcli con up $vpn --ask

expect {
        "GROUP:" { 
                send "$group\r"
                exp_continue
        }
        "Username:" { 
                send "$user\r"
                exp_continue
        }
        Password: {
                send_user "\nProvide RSA OTP:"
                expect_user -re ":(.*)\r"
                set otp $expect_out(0,string)  <--------- Error!!!
                set pass "$secret$otp"
                send "$pass\r"
                exp_continue
        }
        "Connection successfully activated" {
                send_user "Connected to VPN\r"
        }
        "Login failed" {
                send_user "Login failed\r"
                exp_continue
        }
        "already active" {
                send_user "Already connected to VPN\r"
        }
}
exit

I included an arrow to what I think is the source of the problem because when I run this script, it looks like expect_out contains Password:. Given what i read in man page I imagined that the buffer is cleared each time new match is made and therefore it contains string matched in most recent expect clause( in this case expect_user -re ":(.*)\r"), however it seems that I'm wrong. I did not see any notes saying that expect_user contents need to be accessed using different function like interact does, so I'm not sure where did my password go?

Thank you

1 Answers

When using send_user you will not see an echo of what you write, unlike when you send to the spawned command, so you will never match : unless the user explicitly types it. Also, you will read newline, not carriage-return. So use

expect_user -re "(.*)\n"

to get the user input and $expect_out(1,string) (1 not 0) will contain the input.

What you are observing is a timeout in your expect_user, which is why the variable is not updated and still contains the old match.


To protect yourself against unseen timeouts, you could set up a "global" timeout check by starting with

proc abort { } { send_user "Timeout!" ; exit 2 }
expect_before timeout abort

The expect_before is done "before" each expect, but also before each expect_user, it seems.

Related