Expect, Interact and Then Again Expect

expect, interact and then again expect

You can read (expect_user) the user's password by yourself and then send it to the spawn'ed program. For example:

[STEP 101] # cat foo.exp
proc expect_prompt {} \
{
global spawn_id
expect -re {bash-[.0-9]+(#|\$)}
}

spawn ssh -t 127.0.0.1 bash --noprofile --norc
expect "password: "

stty -echo
expect_user -timeout 3600 -re "(.*)\[\r\n]"
stty echo
send "$expect_out(1,string)\r"

expect_prompt
send "exit\r"
expect eof
[STEP 102] # expect foo.exp
spawn ssh -t 127.0.0.1 bash --noprofile --norc
root@127.0.0.1's password:
bash-4.3# exit
exit
Connection to 127.0.0.1 closed.
[STEP 103] #

Difference between interact, expect and exit in expect script

exit does what you think it does: exit the script immediately

interact returns control to the human: if there are things to enter manually after the script gives the auth user and passwd, the human must enter them.

expect watches the spawned process for patterns, and when a pattern is matched the script proceeds to the next instruction.

expect Interact reads the matched line over and over again without moving onward

Imagine you're giving instructions to someone:

  1. if you see a red light then stop
  2. if you see a red light then go

You wouldn't be surprised if they get confused. That's what you're doing to Expect with multiple -re $prompt branches. However Expect just takes the first instruction you tell it.

Try this:

    interact {
-o
-re $prompt {
send "export VARIABLE1=\"$working_dir\"\r"
send "issue-test-command -config $config -module $tcl_test\r"
}
}

BTW:

  1. you're missing exp_continue after send "yes\r"
  2. you're sending the wrong number of double quotes on the export command

How do I tell expect that I have finished the interactive mode?

Your problem is two-fold...

  1. You should interact with an explicit return, and give it some way to know you've released control... in this case, I use three plus signs and hit enter.

  2. After you return control, the script will need to get the prompt again, which means the first thing you do after returning control to expect is send another \r. I edited for what I think you're trying to do...

Example follows...

#!/bin/bash  
set timeout -1

expect -c "

spawn telnet $IP $PORT1
sleep 1
send \"\r\"
send \"\r\"
expect Prompt1>
interact +++ return

send \"\r\"
expect {
Prompt2> {send \"dir\r\" }
}
"

How to set the Interact timeframe for expect

This uses the Tcl after command to schedule code to run after X milliseconds

spawn -noecho telnet IP

proc get_user_time_in_milliseconds {username} {
# checks here
set limit 5000 ;# => 5 seconds
return $limit
}

proc byebye {spawn_id} {
send_user "\n\nYour time is up. Bye!\n"
exp_close $spawn_id
exit
}

after [get_user_time_in_milliseconds $env(LOGNAME)] [list byebye $spawn_id]

# rest of expect code goes here.
interact

Expect: exit/finish/close interact on returned output

Here's an example for you. Expect will be controlling this shell script:

#!/bin/sh
PS3="your choice? "
select answer in foo bar baz quit; do
case $answer in
quit) echo "bye bye"; break;;
*) echo "you chose $answer";;
esac
done
echo "out of select loop: hit enter..."
read x
echo "exiting ..."

The expect program is:

#!/usr/bin/env expect

spawn sh test.sh

set keyword "out of select loop"

# signal handlers: ignore these (can't trap SIGKILL)
trap SIG_IGN {HUP INT TERM QUIT}

# interact until a certain pattern is seen
# in the output (-o) of the process
interact {
\003 {
# prevent the user sending SIGINT to the spawned process
puts "don't press Ctrl-C again"
}
-o $keyword {
send_user "you quit\n"
send_user $keyword
return # "return" exits the interact command
}
}

# do other stuff here, for example, hit enter to allow the
# shell script to terminate
expect -re {\.\.\.\s*$}
send_user "... hitting enter ...\n"
send -- \r
expect eof

The expect man page is rough going. You'll be much happier with the Exploring Expect book.

interact doesn't work when expect script contents are piped into

I just come up with a way to do that. See this example:

$ cat foo.exp
if {[catch {
# Close stdin and reopen it as /dev/tty. Then we are
#+ connecting to a tty.
close stdin
open /dev/tty
} err]} {
puts stderr $err
exit 1
}

# Without this, inputed lines will be echo'ed twice.
set stty_init -echo
spawn bash --noprofile --norc

expect -re {bash-[.0-9]+(#|\$) }
interact

exit
$ expect < foo.exp
spawn bash --noprofile --norc
bash-4.2# ptree $$
705 screen -T dtterm -U
24164 bash
26826 expect
26827 bash --noprofile --norc
26830 ptree 26827
bash-4.2# exit
exit
$

To make it more smart we need to check if stdin is a tty or not at the beginning.

Using two interact in a Expect script

I have resolved the problem:

#!/usr/bin/expect
set timeout -1

set arg1 [lindex $argv 0]
spawn ssh -p 24 my_username@my_linux.domain.com
expect "#"
send "go $arg1 \n"
expect "sername:"
send "my_username\n"
expect "assword:"
send "my_password\n"
expect "#"
interact timeout 5 return
send "\n"
expect "root@my_linux:~#"
send "exit\n exit\n"
interact

Explanation: I added a few lines:

# This prevents commands from timing out (default timeout is 10 seconds).
set timeout -1

# When I type something, the timeout is ignored, but when I'm not typing,
# it waits 5 seconds and then continues.
interact timeout 5 return
send "\n"
expect "root@my_linux:~#"
send "exit\n exit\n"

Expecting the Unexpected in Expect

Try like this:

set timeout 10; # set a reasonable timeout

# expect and send username/password ...

set success 0
set err_msg ""
expect {
"Login success!" {
set success 1
}
eof {
set err_msg $expect_out(buffer)
}
timeout {
expect *
set err_msg $expect_out(buffer)
}
}

if {! $success} {
send_mail $err_msg
exit 1
}


Related Topics



Leave a reply



Submit