Expect script, help handling multiple options in order, and only once - tcl

expect {
"Error: 1*#" {
send_user "\nSpecific Error\n"
}
"Error: *#" {
send_user "\nError Happened\n"
}
"*#" {
send_user "\nNo Error\n"
}
}
Basically what I'm looking to happen is
Check for text "Error: 1", anything between and then the "#" prompt.
If found, send first message to the user
If that's not found check for "Error:" anything at all, and then the "#" prompt.
If found, send an error message to the user
If none of that is met, check for just the "#" prompt
If found, send a message to the user.
I can't reliably get it to work in that order and ONLY do one of those.
Any tips?

You can simply check for the regular expression pattern .*# and upon matching you can look for the desired words.
expect -re ".*#"
set match $expect_out(0,string)
if {[string first "Error: 1" $match]!=-1} {
# scenario 1
} elseif {[string first "Error:" $match]!=-1} {
# scenario 2
} else {
# scenario 3
}
If you really want all-in-one regular expression to match, instead of checking it using if condition, then you can use the following pattern,
(Error:)?\s*(1)?.*#
You have to check whether expect_out(1,string) and expect_out(2,string) exists or not. This is to ensure it contains the desired word.

Related

How to get responses in expect (tcl)

I am trying to query bluetoothctl using expect (tcl), but I cannot seem to get the bluetoothctl responses saved to a variable for processing with tcl.
For example:
spawn bluetoothctl
exp_send "scan on\n"
expect {
-re {*NEW*} {
set new $expect_out(0,string)
puts "scan - found $new"
exp_continue
}
timeout {
exp_send "scan off\n"
exp_send "quit\n"
close
wait
puts "EXPECT timed out"
}
}
The result of the above is along the lines of:
[bluetooth]# scan on
Discovery started
[CHG] Controller 10:08:B1:57:35:62 Discovering: yes
[NEW] Device EB:06:EF:34:04:B7 MPOW-059
[bluetooth]#
EXPECT timed out
So nothing is output until expect is closed. I have been trying this all day with different combinations but - I am stuck. Any help would be appreciated. Thanks
Edit: changed the regex to (.NEW.) and that works. So now I get:
[bluetooth]# scan on
Discovery started
[CHG] Controller 10:08:B1:57:35:62 Discovering: yes
[NEW] Device EB:06:EF:34:04:B7 MPOW-059
[bluetooth]# scan - found scan on
Agent registered
[bluetooth]# scan on
Discovery started
[CHG] Controller 10:08:B1:57:35:62 Discovering: yes
[NEW
which is everything except the bit that I wanted to retrieve viz:
[NEW] Device EB:06:EF:34:04:B7 MPOW-059
That regular expression looks syntactically wrong. If you did {.*NEW.*} then it might work. Assuming that those three letters are actually being output by bluetoothctl with no control characters mixed in. (It'd be weird to do that, but some code is weird…)
Apart from that, have you tried the diagnostic mode for expect? Pass the -d flag to the expect program when you start it to get lots of output about what it is really seeing and looking for.
So the answer appears to be:
The expect_out(buffer) is cleared by a puts statement
Find all the possible responses expected making sure that the expected response specifies the whole line.
Save the buffer in a variable if required
Issue a puts statement to clear the buffer
So:
expect {
"Hello" {
puts "$expect_out(buffer)"
exp_continue
}
-re (How.*) {
set answer $expect_out(buffer)
if {$answer == "How are you"} {
exp_send "Well thank you"
}
}
or, in the example above:
expect {
"Discovery started" {
puts $expect_out(buffer)
exp_continue
}
-re (.CHG.*) {
puts $expect_out(buffer)
exp_continue
}
-re (.NEW.*) {
set new $expect_out(buffer)

need help in eliminating race condition in my code

My code is running infinitely without coming out of loop.
I am calling expect script from shell script, that is working fine,
the problem here is script is not coming out of timout {} loop.
can someone help me in this regard.
spawn ssh ${USER}#${MACHINE}
set timeout 10
expect "Password: "
send -s "${PASS}\r"
expect $prompt
send "cmd\r"
expect $prompt
send "cmd1\r"
expect $prompt
send "cmd2\r"
expect $prompt
send "cmd3\r"
expect $prompt
send "cmdn\r"
#cmdn --> is about running script which takes around 4 hours
expect {
timeout { puts "Running ....." #<--- script is nout coming out of loop its running infinitely
exp_continue }
eof {puts "EOF occured"; exit 1}
"\$.*>" { puts "Finished.." ; exit 0}
}
The problem is that your real pattern, "\$.*>", is being matched literally and not as a regular expression. You need to pass the -re flag for that pattern to be matched as a RE, like this (I've used more lines than ; chars as I think it is clearer that way, but YMMV there):
expect {
timeout {
puts "Running ....."
exp_continue
}
eof {
puts "EOF occured"
exit 1
}
-re {\$.*>} {
puts "Finished.."
exit 0
}
}
It's also a really good idea to put regular expressions in {braces} if you can, so backslash sequences (and other Tcl metacharacters) inside don't get substituted. You don't have to… but 99.99% of all cases are better that way.

Expect script file name too long when writing to a file

I'm new to coding and this is my first script. It works up to the point where it has to open(create) the file to write in. It then fails giving me an error somepassedNickvariable.txt file name too long. I run it as the user "qbot".
Sample output for the first expect:
2016-08-05T23:32:42 73600,565 INF Chat: 'Quadro': !ustawdomek
Sample output for the second one:
1. id=51890, Pepesza, pos=(473,1, 42,1, 1223,7), rot=(-66,1, 104,1, 0,0), remote=True, health=97, deaths=6, zombies=138, players=1, score=109, level=35, steamid=xxx, ip=xx.xx.xx.xx, ping=111
2. id=1141, Quadro, pos=(465,6, 87,1, -624,1), rot=(-30,9, 1620,0, 0,0), remote=True, health=187, deaths=1, zombies=525, players=0, score=520, level=84, steamid=xxx, ip=xx.xx.xx.xx, ping=24
Total of 6 in the game
Any help would be greatly appreciated as I can't get it to work by myself.
#!/usr/bin/expect
set timeout -1
spawn telnet localhost 8081
expect "Please enter password" {sleep 3; send "blahblah\r" }
while {1} {
expect \
{
"*INF Chat: '*': !ustawdomek" {
regexp {'(.+)':\s\!ustawdomek} $expect_out(buffer) match Nick;
set listplayers "*Total of * in the game";
send "lp\r"
expect $listplayers {
regexp "$Nick\,\\spos\=\\(\(\(\[-\]\?\\d+\)\,\\d\,\\s\(\[-\]\?\\d+\)\,\\d\,\\s\(\[-\]\?\\d+\)\,\\d\)\\)" $expect_out(buffer) match lok lok1 lok2 lok3
set file [open "/home/qbot/domki/$Nick.txt" w]
puts $file "$lok1 $lok2 $lok3"
close $file
send "pm $Nick \"\[QBOT\]Blahblah\"\r" }
}
timeout {break}
eof {break}
}
}
I don't entirely understand this script's syntax. But I guess it has something to do with the regexp {'(.+)':\s\!ustawdomek} $expect_out(buffer) match Nick;
.+ as such is a greedy regular expression and should be avoided as long as possible. I think it's trying to match a very large filename. Try to use a simpler regexp that allows only word matches and possibly limit the size of filename. For instance, \w{1,25} or something like that.

linux - telnet - script with expect

I was writing an expect-script which communicate with a server via telnet, but right now i need to evaluate the reply from server.
Usage:
./edit.expect
EXPECT script:
#!/usr/bin/expect<br>
spawn telnet ip port
expect "HUH?"
send "login testuser pass\r"
expect "good"
send "select 1\r"
expect "good"
send "me\r"
expect "nick=testuser id=ID group=testgroup login=testuser"
send "edit id=ID group=3\r"
expect "good"
send "quit\r"
If i send the command "me" i get a reply from the server which i need to evaluate.
The reply from server looks like this example... "nick=NICK id=ID group=GROUP login=LOGIN".
How do i extract the id of the reply and use it in a send-command?
I hope you could help me with that. Thanks a lot!
You can try this way too.
set user_id {}
expect -re {nick=(.*)\s+id=(.*)\s+group=(.*)\s+login=(.*)\n} {
#Each submatch will be saved in the the expect_out buffer with the index of 'n,string' for the 'n'th submatch string
puts "You have entered : $expect_out(0,string)"; #expect_out(0,string) will have the whole expect match string including the newline
puts "Nick : $expect_out(1,string)"
puts "ID : $expect_out(2,string)"
puts "Group : $expect_out(3,string)"
puts "Login : $expect_out(4,string)"
set user_id $expect_out(2,string)
}
send "This is $user_id, reporting Sir! ;)"
#Your further 'expect' statements goes below.
You can customize the regexp as per your wish and note the use of braces {} with -re flag in the expect command.
If you are using braces, Tcl won't do any variable substitution and if you need to use variable in the expect then you should use double quotes and correspondingly you need to escape the backslashes and wildcard operators.
expect lets you match the incoming strings with regular expressions and get the submatches in the expect_out() array. In your example, you could use
send "me\r"
expect -re {nick=([^ ]*) id=([^ ]*) group=([^ ]*) login=([^ ]*)}
set nick $expect_out(1,string)
set id $expect_out(2,string)
set group $expect_out(3,string)
set login $expect_out(4,string)
puts "GOT nick: $nick id: $id group: $group login: $login"
send "edit id=$id group=3\r"
etc...
EDIT: string must be in {} to avoid command expansion

Tcl TCL_OK usage example

Based on the help page for the Tcl catch command, I was trying to use the specified result variables like TCL_OK or TCL_ERROR. However, there is some problem with the syntax of how those variables should be accessed. Does anyone have a code example that uses those variables instead of magic numbers?
From my experience, you want to use the numerical values, not the names. The names are used when programming in the C api but, from Tcl, one just uses the numbers.
set code [catch {
my script
} result]
switch -exact -- $code {
0 {
puts "normal command completion"
}
1 {
puts "code threw an error (ie: error 'wtf')"
}
2 {
puts "code used 'return' command normally"
}
3 {
puts "code used 'break' command"
}
4 {
puts "code used 'continue' command"
}
}