How to send command when no match using expect? - expect

Is it possible to send a default command when expect doesn't match anything?
eg I want to send enter until a match is made?
set timeout 60
expect {
timeout {
puts "Failed to find foo"
exit 1
}
"foo" {
exit
}
default {
send "\r"
}
}

You can expect the line ending to send a carriage return: expect translates line endings to \r\n
expect {
timeout {
puts "Failed to find foo"
exit 1
}
"foo" {
exit
}
"\r\n" {
send "\r"
exp_continue
}
}

Related

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 to handle errors from passwd in expect script?

I'm trying to create an expect script to update a user's password non-interactively. New to expect and shell scripting. I want to be able to capture and handle errors encountered. For example, if I'm running passwd normally, and my new password isn't acceptable for some reason, I might see a message like "BAD PASSWORD: The password is too similar to the old one".
The problem is that, in the expect script, after send $new_pswd\r with an invalid password, expect "BAD PASSWORD:*" is failing because the next output from passwd is " ".
I would like to capture the bad passwords and the message so that I can return those to the calling process.
The use case here is to allow individual users who may have multiple accounts to update all their accounts with the same password in one go. This script would be called once for each account.
#!/usr/bin/expect -df
# THIS SCRIPT WON'T WORK
set old_pswd $::env(PSWD1)
set new_pswd $::env(PSWD2)
spawn passwd
expect "*ssword:" { send "$old_pswd\r" }
expect "*ssword:" { send "$new_pswd\r" }
expect {
"*ssword:" {
puts "Expected\r"
send "$new_pswd\r"
exit 0
}
"BAD PASSWORD:*" {
puts "BAD PASSWORD\r"
send \x03
exit 1
}
"*" {
puts "Unexpected\r"
send \x03
puts $expect_out(buffer)
exit 1
}
}
UPDATE: I learned a lot about expect since yesterday and wound up solving the problem and a couple others in the script.
The first problem was that I was expecting the password prompt without a space after the colon, so all subsequent expect commands were trying to match against the as-yet-unconsumed space character, which matched against "*", and terminated the script before passwd ever sent its BAD PASSWORD message.
The second problem was trying to match *ssword: before "BAD PASSWORD". The prompt for new password would always match before the BAD PASSWORD message, because passwd was sending "BAD PASSWORD: message message message\r\nNew password: "
Here is a script that now works and is wrapped in an su. Whatever process is calling this script needs to clean up the environment variables afterward. The idea would be to set those securely in advance so that credentials don't have to be passed on command lines. This script is intended to be called from the user's account once for each other account that belonged to them:
#!/usr/bin/expect -f
# THIS SCRIPT WORKS
set old_pswd $::env(PSWD1)
set new_pswd $::env(PSWD2)
set new_user $::env(USER1)
spawn su $new_user
expect "*ssword: " { send "$old_pswd\r" }
expect "$ " { send "passwd\r" }
expect "*ssword: " { send "$old_pswd\r" }
expect "*ssword: " { send "$new_pswd\r" }
expect {
"BAD PASSWORD: " {
expect {
"dictionary" { set exit_code 1 }
"shorter than" { set exit_code 2 }
"too similar" { set exit_code 3 }
"No password" { set exit_code 4 }
"*" { set exit_code 99 }
}
send \x03
expect "$ " { send "exit\r" }
exit $exit_code
}
"*ssword: " {
send "$new_pswd\r"
expect "$ " { send "exit\r" }
exit 0
}
}

Expect doesn't recognize command "timeout" in loop

I have an expect script to check login status, if console showing "poplar login", script will send username (no password) and wait 180s for system ready.
Once system ready it will check is the console showing "root#poplar" if yes, it will break and send success message. If not, expect will be timeout and go back to "poplar login"
my script
set timeout 2
expect {
"poplar login" {
send "root\r";
sleep 180; send "\r" ;
send "\r" ;
exp_continue
}
"root#poplar"
timeout { send "\r" ; exp_continue }
}
send_user "login success!\n"
Somehow the timeout is always not read by expect and the log is like follow
expect: does"\r\n\u001b[r\u001b[m\u001b[2J\u001b[H\u001b[?7h\u001b[?1;4;6l
\u001b[?1049h\u001b[4l\u001b[?1h\u001b=\u001b[0m\u001b(B\u001b[1;70r\u001b[H
\u001b[2J\u001b[H\u001b[2J"(spawn_id exp5) match glob pattern "poplar login"? no
"root#poplar"? no
" send "\r" ; exp_continue "? no
expect: timed out
login success!
send: sending "reboot\r" to { exp5 }
Please assist, thanks
Change
"root#poplar"
timeout { send "\r" ; exp_continue }
to
"root#poplar" {}
timeout { send "\r" ; exp_continue }
or it would be handled as
"root#poplar" timeout
{ send "\r" ; exp_continue }
That's why you see the debug message
" send "\r" ; exp_continue "? no
because the part { send "\r" ; exp_continue } is handled as a PATTERN.

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.

Expect: Prompt extending to next line

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

Resources