expect not able compile regular expression pattern: - expect

I am running expect from Jenkins job and it is giving below mentioned error.
Not sure what is wrong in code, I am fairly new with expect. Appreciate help!
Error Message:
send: sending "date\r" to { exp3 }
couldn't compile regular expression pattern: quantifier operand invalid
while executing
"expect -re "*2016*" "send_user ""
Expect Code:
#!/usr/local/bin/expect
exp_internal 1
set timeout 20
spawn ssh USER_ID#Server_ID
expect "*assword:" {send "password\\r"}
expect -re "Login (incorrect|invalid)" {send_user "\nlogin failed\n" }
expect -re "Last login:*" {send_user "\nlogin successful\n" }
send "cd some_directory\\r"
send "pwd\\r"
expect -re "some_directory" {send_user "\n-->location verified\n"}
send "date\\r"
expect -re "*2016*" {send_user "\ndate verified\n"}

Related

Expect script continues to run even the expect does not match anything

#!/usr/bin/expect
spawn ssh admin#10.10.10.10 -o StrictHostKeyChecking=no
expect "5555:"
send "$password\n"
expect "% "
send "exit\n"
No matter what value wrote on expect "5555:" this command still working and I have ssh connection.
You should set a timeout and an action upon the timeout:
#!/usr/bin/expect
set timeout 5
spawn ssh admin#10.10.10.10 -o StrictHostKeyChecking=no
expect {
"5555:" { }
timeout { exit 2 }
}
send "$password\n"
expect "% "
send "exit\n"
Some extra info: the default timeout is 10 seconds. While expecting something, if the pattern has not been seen after $timeout seconds, the expect command without a "timeout" pattern simply returns and the script continues.

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.

Linux expect send weird constructions

I am trying to figure out how expect working. AFAIK the expect script consists of "expect" and "send" statements. So for each approporiate "expect" statement which appears on screen the "send" statement is called. Also the command "interact" means that controlling is passed back to user and he is able to interact with the terminal. Correct me if I am wrong. Those two statements works like a charm.
1st:
#!/usr/bin/expect
spawn ssh -q localhost;
# Handles following message: "Are you sure you want to continue connecting (yes/no)?"
expect "yes";
send "yes\r";
interact;
2nd:
#!/usr/bin/expect
spawn ssh -q localhost;
# Handles following message: "pista#localhost's password:"
expect "assword";
send "password\r";
interact;
I've found on internet that something like following code should combine those two examples into one:
#!/usr/bin/expect
spawn ssh -q localhost "uname -a";
expect {
"*yes/no*" { send "yes\r" ; exp_continue }
"*assword:" { send "password\r"; interact }
}
But this example exits immediatelly after sucesfull login (seems like "interact" is not workig here, see output bellow)
[pista#HP-PC .ssh]$ ./fin.exp
spawn ssh -q localhost uname -a
pista#localhost's password:
Linux HP-PC 3.6.6-1.fc16.x86_64 #1 SMP Mon Nov 5 16:56:43 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
[pista#HP-PC .ssh]$ set | grep SHLV
SHLVL=2
Three questions:
What those weird expect syntax mean, only possible explaination to me is that there is no emphasis on patterns order in this "big" expect ?
Can you please clarify what is exactly exp_continue doing, seems to me like "goto" statement to expect which invoked this ?
Why is not interact working here ?
Many thanks
1. This syntax means that you use consecutive expect statements, it's easier. For example, this will try SSH or telnet if SSH fails
#!/usr/bin/expect
set remote_server [lrange $argv 0 0]
set timeout 10
spawn ssh -M username#$remote_server
while 1 {
expect {
"no)?" {send "yes\r"}
"denied" {
log_file /var/log/expect_msg.log
send_log "Can't login to $remote_server. Check username and password\n";
exit 1
}
"telnet:" {
log_file /var/log/expect_msg.log
send_log "Can't connect to $remote_server via SSH or Telnet. Something went definitely wrong\n";
exit 2
}
"failed" {
log_file /var/log/expect_msg.log
send_log "Host $remote_server exists. Check ssh_hosts file\n";
exit 3
}
timeout {
log_file /var/log/expect_msg.log
send_log "Timeout problem. Host $remote_server doesn't respond\n";
exit 4
}
"refused" {
log_file /var/log/expect_msg.log
send_log "Host $remote_server refused to SSH. That is insecure.\n"
log_file
spawn telnet $remote_server
}
"sername:" {send "username\r"}
"assword:" {send "password\r"}
"#" {break}
}
}
2. exp_continue is telling expect to "continue to expect", that is to continue with the event processing. Without this instruction your expect { ... } block will stop. In the example above it is the line:
"#" {break}
First it breaks from the while loop, and then without exp_continue it stops executing expect { ... } block and going for the next instructions (not shown in the example above).
3. You have an error in your code. I've slightly modified your code, to make it work.
#!/usr/bin/expect
spawn ssh -q localhost
expect {
"*yes/no*" { send "yes\r" ; exp_continue }
"*assword:" { send "password\r"; exp_continue;}
"~" {send "uname -a\r"; interact;}
}

Making decisions on expect return

I am trying to create an expect script which will send a different password string based on the "expect"
Condition A: If a cisco device has not been setup with a username then the first prompt will simply be "Password:" - then it should use passwordA (no username)
Condition B: If it has been setup with a username then the prompt will be "Username:" followed by "Password:" - then it should use Username and PasswordB
#!/bin/bash
# Declare host variable as the input variable
host=$1 
# Start the expect script
(expect -c "
set timeout 20
# Start the session with the input variable and the rest of the hostname
spawn telnet $host
set timeout 3
if {expect \"Password:\"} {
send \"PasswordA\"}
elseif { expect \"Username:\"}
send \"UsersName\r\"}
expect \"Password:\"
log_user 0
send \"PasswordB\r\"
log_user 1
expect \"*>\"
# send \"show version\r\"
# set results $expect_out(buffer)
#expect \"Password:\"
#send \"SomeEnablePassword\r\"
# Allow us to interact with the switch ourselves
# stop the expect script once the telnet session is closed
send \"quit\r\"
expect eof
")
You're doing it wrong. :)
The expect statement doesn't look to see what comes first, it waits until it sees what you ask for (and times out if it doesn't arrive in time), and then runs a command you pass to it. I think you can use it something like the way you're trying to, but it's not good.
expect can take a list of alternatives to look for, like a C switch statement, or a shell case statement, and that's what you need here.
I've not tested this, but what you want should look something like this:
expect {
-ex "Username:" {
send "UsersName\r"
expect -ex "Password:" {send "PasswordB\r"}
}
-ex "Password:" {send "PasswordA\r"}
}
In words, expect will look for either "Username:" or "Password:" (-ex means exact match, no regexp), whichever comes first, and run the command associated with it.
In response to the comments, I would try a second password like this (assuming that a successful login gives a '#' prompt):
expect {
-ex "Username:" {
send "UsersName\r"
expect -ex "Password:" {send "PasswordB\r"}
}
-ex "Password:" {
send "PasswordA1\r"
expect {
-ex "Password:" {send "PasswordA2\r"}
-ex "#" {}
}
}
}
You could do it without looking for the # prompt, but you'd have to rely on the second Password: expect timing-out, which isn't ideal.

Expect within a Bash script

I wrote a Bash script with Expect within, to connect to a terminal server and clear lines.
I am unable to figure out the error I am getting, as in I have given all the braces necessary. I also do not understanding the couldn't read file "line": no such file or directory error.
How can I fix this?
My script:
#!/bin/bash
VAR=$(expect -c "
spawn telnet 1.1.1.1
expect {
"Password:" { send "password\r" ; exp_continue}
"Prompt>" { send "en\r" ; exp_continue}
"Password:" { send "password\r" ; exp_continue}
"Prompt#" {send "clea line 10\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "clea line 11\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "exit\r" }
}
")
echo $VAR
Its output:
missing close-brace
while executing
"expect"
couldn't read file "line": no such file or directory
The first problem is that the shell does not interpret nested double quotes as you might like. The easiest way to fix this is to put the Expect program in single quotes. This will be sufficient as long as there are no single quotes in the Expect program itself.
The next problem you will run into is that having all the patterns and actions in a single expect command will process them in parallel. What is actually happens is that the first Password: pattern will match any time it sees that string (i.e. even for the admin password the second time around). This will be a problem if the two passwords need to be different. At a minimum, identical patterns will need to go into separate expect commands so that they can be executed sequentially. This problem also affects the Prompt# pattern where you look for it three times and want to send three different responses.
Later, you will get an error after you send the first clear command. Expect interprets square brackets inside double quotes in a way that is similar to how shells interpret $() or `` (i.e. command substitution). You will see an error like this:
invalid command name "confirm"
while executing
"confirm"
invoked from within
"expect {
⋮
It is trying to run confirm as a Tcl (or Expect) command. You can use curly brackets ({}) to prevent Tcl from making this interpretation. Furthermore, expect patterns are treated as “glob” expressions by default (i.e. like shell wildcards), so even if you write {[confirm]} as the pattern, it will still not be used for an exact string match (it would match any single character c, o, n, f, i, r, or m). You must use the -ex flag to mark the pattern for exact matching.
Fix these issues, drop some of the unnecessary quoting, and you might end up with something like this:
#!/bin/sh
VAR=$(expect -c '
proc abort {} {
puts "Timeout or EOF\n"
exit 1
}
spawn telnet 1.1.1.1
expect {
Password: { send "password1\r" }
default abort
}
expect {
Prompt> { send "en\r"; exp_continue }
Password: { send "password2\r" }
default abort
}
expect {
Prompt# { send "clea line 10\r"; exp_continue }
-ex {[confirm]} { send "Y\r" }
default abort
}
expect {
Prompt# { send "clea line 11\r"; exp_continue }
-ex {[confirm]} { send "Y\r" }
default abort
}
expect {
Prompt# { send "exit\r"; exp_continue }
timeout abort
eof
}
puts "Finished OK\n"
')
echo "$VAR"
The problem is that that the double quotes in the Expect script are treated as the end of the Expect script. Try mixing single quotes with double quotes:
#!/bin/bash
VAR=$(expect -c '
spawn telnet 1.1.1.1
expect {
"Password:" { send "password\r" ; exp_continue}
"Prompt>" { send "en\r" ; exp_continue}
"Password:" { send "password\r" ; exp_continue}
"Prompt#" {send "clea line 10\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "clea line 11\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "exit\r" }
}
')
#Chris: I incorporated the changes you suggested and my code is working now.
However, I had to make two more changes stated below:
The single quote which you mentioned prevents parameter substitution. For example, I cannot write $IP in place of 1.1.1.1. Hence, to get around this, I removed the single quotes and replaced with double quotes. As you mentioned nested doubles quotes are not interpreted by Bash which is true. Hence I rewrote the inside double quotes as
send \"password1\r\"
That is adding backslashes before the double quotes inside. This corrects the problem of parameter substitution.
Even after I put two/three actions within a single expect command, as they run in parallel I still faced issues. So taking your suggestion, I put each of the action in a separate Expect command. Something like:
expect {
Prompt> { send "en\r"; exp_continue }
}
expect {
Password: { send "password2\r" }
}

Resources