Expect: Prompt extending to next line - shell

I am trying to install a software which is a shell script file. I am using expect to do the silent installation.
There is a strange line during the installation of the software where the prompt goes to the next line like this below.
ENTER AN ABSOLUTE PATH, OR PRESS <ENTER> TO ACCEPT THE DEFAULT
:
So I have tried with these 2 options, but it's NOT working!
1. expect " : " { send "/home/tester/IDir\r" }
2. expect "ENTER AN ABSOLUTE PATH, OR PRESS <ENTER> TO ACCEPT THE DEFAULT\
: " { send "/home/tester/IDir\r" }
Expect File
#!/usr/bin/expect -f
#Taking arguments from test.sh file
set File [lindex $argv 0]
set IDir [lindex $argv 1]
spawn sh /home/tester/$File
expect "PRESS <ENTER> TO CONTINUE: " { send "\n" }
exp_internal 1
expect -re "TO ACCEPT THE DEFAULT\r\n.*?:" { send "/home/tester/IDir\r" }
#expect " : " { send "/home/tester/IDir\r" }
expect "IS THIS CORRECT? (Y/N): " { send "Y\n" }
expect "PRESS <ENTER> TO CONTINUE: " { send "\n" }
sleep 2
expect "ENTER THE NUMBER OF THE DESIRED CHOICE: " { send "2\n" }
expect "PRESS <ENTER> TO EXIT THE INSTALLER: " { send "\n" }
sleep 2
expect eof
test.sh file
dir=James/SDD
cd /home/tester
file=`ls | grep xx_yy*_linux_x86-64.bin`
expect script.exp $file $dir

After analyzing the logs, realized that your expect code is getting timed out for most of the cases.
# This timeout is meant for the first expect word 'PRESS <ENTER> TO CONTINUE', so it is proceeding to the next expect word 'TO ACCEPT THE DEFAULT.*?:'
expect: timed out
Gate keeper glob pattern for 'TO ACCEPT THE DEFAULT
.*?: ' is 'TO ACCEPT THE DEFAULT
You need to add the proper timeout handling for the expect statement. Default timeout is 10 seconds. You can change it in a way as follows,
set timeout 120; # Now, timeout value is 2 min
I can see that you have used sleep in some places, in the same way, do that for earlier expect commands as well. (Using timeout would be better in such cases than sleeping.)
#Taking arguments from test.sh file
set File [lindex $argv 0]
set IDir [lindex $argv 1]
# A common handler for timeout.
# Customize it as per your need.
proc my_timeout_handler {} {
puts "Timeout happened :("
exit 1
}
spawn sh /home/tester/$File
set timeout 300; # 5 mins.
expect {
"PRESS <ENTER> TO CONTINUE: " { send "\n" }
timeout {my_timeout_handler}
}
expect {
-re "TO ACCEPT THE DEFAULT\r\n.*?:" { send "/home/tester/IDir\r" }
timeout {my_timeout_handler}
}
expect {
"IS THIS CORRECT? (Y/N): " { send "Y\r" }
timeout {my_timeout_handler}
}
expect {
"PRESS <ENTER> TO CONTINUE: " { send "\r" }
timeout {my_timeout_handler}
}
expect {
"ENTER THE NUMBER OF THE DESIRED CHOICE: " { send "2\r" }
timeout {my_timeout_handler}
}
expect {
"PRESS <ENTER> TO EXIT THE INSTALLER: " { send "\r" }
timeout {my_timeout_handler}
}
expect {
eof {puts "Program completed"}
timeout {my_timeout_handler}
}

Related

issues with spawn id <idx> not open when executing script from iTerm2

I am trying to write a script and execute under iTerm2 profile. However whenever I open the new tab i run into this error. but I execute the same script on Terminal and no problem for it. anyone may know why?
expect: spawn id exp6 not open
`while { 1 } {
(file "xxxxx" line xx)
....
#!/usr/bin/expect
set username [lindex $argv 1]
set hostname [lindex $argv 2]
set password "abcd"
set timeout 60
# trap SIGWINCH and pass to spawned process
trap {
set rows [stty rows]
set cols [stty columns]
stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH
spawn ssh -o NoHostAuthenticationForLocalhost=yes -o UseKeychain=yes $username#$hostname
while { 1 } {
expect "Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
} "Enter passphrase for key " {
interact
break
} "password:" {
send "$password\r"
}
}
To expect several things simultaneously, don't use while use exp_continue
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"Enter passphrase for key " {
interact
}
"password:" {
send "$password\r"
interact
}
}
If you get the connection prompt, send yes and loop back to the expect statement.
If you get prompted for the passphrase, just drop into interactive mode.
If it's the password prompt, send the password prompt and go interactive.
When the user disconnects, interact will return, the expect statement returns, and the script exits.

Fetch using mtlogin not working on RouterOs 7.1.5

I am currently encountering a problem.
When I want to download a file on a mikrotik in 6.48.6 using mtlogin and fetch tool, it works perfectly and the script waits until the router has finished downloading to send a "quit".
However, when trying the same manipulation on a router in version 7.1.5, the "quit" is sent directly, thus stopping the download because of the letter Q and thus sending "uit" thereafter in the prompt.
The prompts are similar for 6.48.6 and 7.1.5, and even when trying to add expects in the script, the result is the same.
I think the problem is in this part of the code, but don't know how to fix it.
# Run commands given on the command line.
proc run_commands { prompt command } {
global do_interact in_proc
set in_proc 1
# escape any parens in the prompt, such as "(enable)"
regsub -all "\[)(]" $prompt {\\&} reprompt
# handle escaped ;s in commands, and ;; and ^;
regsub -all {([^\\]);} $command "\\1\u0002;" esccommand
regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command
regsub {^;} $command "\u0002;" esccommand
regsub -all {[\\];} $esccommand ";" command
regsub -all {\u0002;} $command "\u0002" esccommand
set sep "\u0002"
set commands [split $esccommand $sep]
set num_commands [llength $commands]
for {set i 0} {$i < $num_commands} { incr i} {
send -- "[subst -nocommands [lindex $commands $i]]\r"
if { [lindex $commands $i] == "/system/reboot"} {
send "y\r"
}
expect {
-re "^\[^\n\r]*$reprompt" {}
-re "^\[^\n\r ]*>>.*$reprompt" { exp_continue }
-re "\[\n\r]+" { exp_continue }
}
}
if { $do_interact == 1 } {
interact
return 0
}
send "quit\r"
expect {
-re "^WARNING: There are unsaved configuration changes." {
send "y\r"
exp_continue
}
"\n" { exp_continue }
"\[^\n\r *]*Session terminated" { return 0 }
timeout { catch {close}; catch {wait};
return 0
}
eof { return 0 }
}
set in_proc 0
}
That's how it looks like
Does anyone have a solution?
I just find the solution in mtlogin at line 625!
foreach router [lrange $argv $i end] {
set router [string tolower $router]
send_user "$router\n"
# Figure out prompt.
set prompt "] > " #Just added a second whitespace after >
# alteon only "enables" based on the password used at login time
set autoenable 1
set enable 0
Hope it's gonna help you

How does Expect script determine contents of expect_out

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
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.

Non-reliable results from Expect when running a remote script

Because my company insists on using sudo I now have to scrap my ssh system I've built for running remote commands.
I have a script that will connect to a server, sudo into the db2 inst owner account and then run a script (previously delivered into /tmp). Maybe 1 in 10 times it will work (outputs 'Hello world' for my test).
Here is the ksh script on the remote server (/tmp/cwow/generic.ksh):
#!/usr/bin/ksh93
echo "Hello world"
[I've also tried adding sleep 5 and wait with mixed results but it doesn't solve the problem]
The expect script I'm running locally is:
#!/usr/bin/expect
set spath /tmp/cwow/generic.ksh
set pass $env(MYEXPECTPASS)
set user $env(MYEXPECTUSER)
if { [llength $argv] != 2 } {
send_user "USAGE: $argv0 host inst\n"
exit
}
set host [lindex $argv 0]
set inst [lindex $argv 1]
set timeout 10
log_user 1
exp_internal 0
eval spawn /usr/bin/ssh -t $user#$host "sudo su - cwow"
expect {
timeout { send_user "TimedOut"; exit }
-glob "assword:" {
send "$pass\r"
expect {
-glob "assword:" {
send "$pass\r"
expect {
-glob " " {
send "/tmp/cwow/generic.ksh\r\n"
expect {
-glob "world" {
send_user "Got it\r"
}
}
}
}
}
}
}
}
I should also note that I never get the 'Got it' message but I don't really need that to work, just curious why it doesn't. What I need to work reliably is for the script to run and, most of the time, it doesn't appear to.
Any ideas for a weak expect user would be greatly appreciated.
(not an answer, just a formatted comment)
You don't need to nest all the expect commands: if you expect a pattern with no action body, the script will continue to the next command. This is more readable, IMO:
expect {
timeout { send_user "TimedOut"; exit }
"assword:"
}
send "$pass\r"
expect "assword:"
send "$pass\r"
expect " "
send "/tmp/cwow/generic.ksh\r"
expect "world"
send_user "Got it\n"
Note, you should send to the spawned process with \r as "hitting Enter". But \n is used for send_user.

How to continue with next command if the remote switch is not responding in an expect script

I'm passing the switch name from a bash script. The bash script iterates through a text file and sends the switch name. Sometimes if the switch is not responding, the expect script gets stuck. I want the script to wait for some time and move to next switch or at least come out. I have used the timeout but with no help.
set timeout 60
.
.
.
expect "*> "
send "ssh -l admin $switchName.XXX.XXXXXX.net switchshow -portname > $filename1\r"
sleep 2
expect {
"*(yes/no)? " {
send "yes\r"
exp_continue
}
"*assword: " {
send "$password\r"
}
default {
exp_continue
}
}
Any suggestion is appreciated.
you want to expect to see the "timeout" keyword:
expect {
timeout {
puts "timed out waiting for $switchName"
}
"*(yes/no)? " {
send "yes\r"
exp_continue
}
"*assword: " {
send "$password\r"
exp_continue
}
eof
}
I also updated your conditions a bit so that you remain in the expect "loop" until the ssh command completes.

Resources