Problems looping to prompt for another password

2019-02-21 03:25发布

问题:

I need some help with an EXPECT script please....

I'm trying to automate a login, prior to accessing a load of hosts, and cater for when a user enters a password incorrectly. I am getting the username and password first, and then validating this against a particular host. If the password is invalid, I want to loop round and ask for the username and password again.

I am trying this :-

(preceding few irrelevant lines omitted)

    while {1} {
            send_user "login as:- "
            expect -re "(.*)\n"
            send_user "\n"
            set user $expect_out(1,string)
            stty -echo
            send_user "password: "
            expect -re "(.*)\n"
            set password $expect_out(1,string)
            stty echo

            set host "some-box.here.there.co.uk"
            set hostname "some-box"
            set host_unknown 0
            spawn ssh $user@$host
            while {1} {
                    expect {
                            "Password:"     {send $password\n
                                            break}
                            "(yes/no)?"     {send "yes\n"}
                            "Name or service not known"     {set host_unknown 1
                                                            break}
                            }
                    }
            if {$host_unknown < 1} {

                    expect {
                            "$hostname#"    {send "exit\r"
                                            break
                                            }
                            "Password:"     {send \003
                                            expect eof
                                            close $spawn_id
                                            puts "Invalid Username or Password - try again..."
                                            }
                            }
                    } elseif {$host_unknown > 0} {

                            exit 0}
            }
    puts "dropped out of loop"

And now I can go off and do lots of stuff to lots of boxes .....

This works fine when I enter a valid username or password, and my script goes off and does all the other stuff I want, but when I enter an invalid password I get this :-

Fred@Aserver:~$ ./Ex_Test.sh ALL

login as:- MyID

password: spawn ssh MyID@some-box.here.there.co.uk
Password:

Password: Invalid Username or Password - try again...
login as:- cannot find channel named "exp6"

while executing  "expect -re "(.*)\n""
invoked from within  "if {[lindex $argv 1] != ""} {
    puts "Too many arguments"
    puts "Usage is:-  Ex_Test.sh  host|ALL"

} elseif {[lindex $argv 0] != ""} {

    while {1} {
    ..."
(file "./Ex_Test.sh" line 3)

Its the line "can not find channel named "exp6" which is really bugging me.

What am I doing wrong? I am reading Exploring Expect (Don Lines) but getting nowhere....

回答1:

Whenever expect is supposed to wait for some word, it will save the spawn_id for that expect process into expect_out(spawn_id).

As per your code, expect's spawn_id is generated when it encounters

        expect -re "(.*)\n"

When user typed something and pressed enter key, it will save the expect's spawn_id. If you have used expect with debugging, you might have seen the following in the debugging output

expect does "" (spawn_id exp0) match regular expression "(.*)\n" 

Lets say user typed 'Simon', then the debugging output will be

expect: does "Simon\n" (spawn_id exp0) match regular expression "(.*)\n"? Gate "*\n"? gate=yes re=yes
expect: set expect_out(0,string) "Simon\n"
expect: set expect_out(1,string) "Simon"
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) "Simon\n"

As you can see, the expect_out(spawn_id) holds the spawn_id from which it has to expect for values. In this case, the term exp0 pointing the standard input.

If spawn command is used, then as you know, the tcl variable spawn_id holds the reference to the process handle which is known as the spawn handle. We can play around with spawn_id by explicitly setting the process handle and save it for future reference. This is one good part.

As per your code, you are closing the ssh connection when wrong password given with the following code

close $spawn_id

By taking advantage of spawn_id, you are doing this and what you are missing is that setting the expect's process handle back to it's original reference handle. i.e.

While {1} { 

    ###Initial state. Nothing present in spawn_id variable ######
    expect "something here"; #### Now exp0 will be created  

    ###some code here ####

    ##Spawning a process now###

    spawn ssh xyz ##At this moment, spawn_id updated

    ###doing some operations###
    ###closing ssh with some conditions###
    close $spawn_id

    ##Loop is about to end and still spawn_id has the reference to ssh process  
    ###If anything present in that, expect will assume that might be current process
    ###so, it will try to expect from that process

}

When the loop executes for the 2nd time, expect will try to expect commands from the spawn_id handle which is nothing but ssh process which is why you are getting the error

can not find channel named "exp6" 

Note that the "exp6" is nothing but the spawn handle for the ssh process.

Update :

If some process handle is available in the spawn_id, then expect will always expect commands from that process only.

Perhaps you can try something like the following to avoid these.

#Some reference variable 
set expect_init_spawn_id 0

while {1} {

    if { $expect_spawn_id !=0 } {
            #when the loop enters from 2nd iteration,
            #spawn_id is explicitly set to initial 'exp0' handle
            set spawn_id $expect_init_spawn_id 
    }

    expect -re "(.*)\n"
    #Saving the init spawn id of expect process
    #And it will have the value as 'exp0'
    set expect_init_spawn_id $expect_out(spawn_id)
    spawn ssh xyz

    ##Manipulations here

    #closing ssh now
    close $spawn_id
}

This is my opinion and it may not be the efficient approach. You can also think of your own logic to handle these problems.



标签: tcl expect