Training, Open Source computer languages
PerlPHPPythonMySQLApache / TomcatTclRubyJavaC and C++LinuxCSS 
Search for:
Home Accessibility Courses Diary The Mouth Forum Resources Site Map About Us Contact
 
20.9.2014 - We have just updated our course layouts and descriptions and added our 2015 schedule.

Expect - Simultaneous pattern matching

Posted by neilvp (neilvp), 5 September 2006
I am attempting to match a number of possible patterns that are being sent in a text file to Stdin using cat.

According to the section 'Pattern-Action Pairs' in "Exploring Expect" Pg 75/76  I can set up

while {1} {
expect \
   {
       "hi"    {Send_Output "HI\r" }  
       "hello" {Send_Output "HELLO\r"}
       "bye"   {Send_Output "BYE\r"}
       timeout {Time_Out; break}
       eof {puts "\rBreaking - EOF\r" ; break}
   }
}
and Expect will simultaneously look for "hi", "hello" and "bye" and take the appropriate action if matched.

So if I send a text file that is

hello
hi
bonjour
bye

I would expect (pun intended) to see on Send_Output

HELLO
HI
BYE

Is my expectation correct?
Because what I get is

HI
BYE

The full code and test file are attached.
Would you please try this on unix and see what happens and if possible explain what is going on and why it is I do not see what I expect to see.

Many thanks

Neil

The Code
********************
#!/bin/sh
#  The line above enables this script to work on both Unix and Windows. (Yeah right)
## ******************************************************************************
##
##  TCL script to test simultaneous Expect statement.
##  " Exploring Expect" Pg 76
##
##  31 Aug 06
##  Neil Peers
##
##  Description
##
##
##  *****************************************************************************


package require Expect


puts "\n\n*******************************************"
puts "Run starting at [clock format [clock seconds] -gmt true -format "%d %b %Y %r %Z"]"
puts "*******************************************\n\n"


set loop 0
# loop is used as an indicator in the output file as to how many
# times the Send_Output routine is called.


set FileToRead hi_test.txt

set TestOutputFile hi_results.txt

set fileID [open $TestOutputFile w]
# $TestOutputFile is the file the script will write to.
# Open and close it to make sure a) it exists and b) it is clear.
close $fileID


# Send_Output opens the output file, writes the inbound string # to it and then closes the file again.

proc Send_Output {sendstring} \
{
   global TestOutputFile
   global loop
   incr loop

   set fileID [open $TestOutputFile a]

   puts $fileID "$loop $sendstring"

   close $fileID
}


if { [catch {open  $TestOutputFile w} fileID] }\
{
   puts "Cannot open $TestOutputFile"
} else \
{
   puts "file $TestOutputFile is open\r"
   close $fileID
   puts "closed it\r"
}


if [catch {spawn cat $FileToRead}  reason]\
{
   puts "Failed to spawn\r"
   puts "Reason $reason\r"
} else {
   puts "spawned"    
}    
   


while {1} \
{
   expect \
   {
       "hi"           {Send_Output "HI\r" }  
       "hello"       {Send_Output "HELLO\r"}
       "bye"        {Send_Output "BYE\r"}
       timeout     {Time_Out; break}
       eof {puts "\rBreaking - EOF\r" ; break}
   }
}


puts "Ending Program"

**************************************
**************************************
Test file

hello
hi
bonjour
bye



Posted by admin (Graham Ellis), 6 September 2006
Your script is checking for each of the possible strings <i>in the order in which they occur in the multiple choice expect statement</i>; I reproduced your reported behavioud then switched to


while {1} \
{
   expect \
   {  
  "hello"  {Send_Output "HELLO\r"}
  "hi"      {Send_Output "HI\r" }  
  "bye"   {Send_Output "BYE\r"}
  timeout     {Time_Out; break}
  eof {puts "\rBreaking - EOF\r" ; break}
   }
}


which gave me


1 HELLO
2 HI
3 BYE


longer explanation ...

Your spawned cat command starts pouring text into a bucket as soon as it's started, then (when it gets a look in on the c.p.u.) the expect command asks ....
1. Do I have a hi?
and it doesn't get any further because, yes, as the time both the hello and the hi have been added to the bucket, so it returns.

Then the expect command carries on and asks
1. Do I have a hi?
2. Do I have a hello?
3. Do I have a bye?
And it gets a match in that last case, so it returns because of the bye

Finally (third loop) the first thing that matches is the eof.

The problem that you are reporting (in essence) is that you're hoping to look for whichever of the incoming strings occurs first in the stream of data that cat's producing, rather than asking for each in turn, and that's going to give you different results if the buffer (bucket) contains several matches when you get round to checking it.

Ironically, your program would probably have produced the results you wanted if you had not used the cat sommand but something that's much slower to send the output through, such as a 'real' program that does a lot of work before outputting each result line.   But in a way, thank goodness you've raised the issue now as that would have been a program that would have sometimes failed on a busy machine or on a different o/s with a different timesharing algortithm - such are typical expect issues  

A solution?

I'm sure that in reality, you're NOT trying to cat a file with various greetings in it, but rather to process each line of an incoming flow in different ways, with additional (fallback) traps for timeout and eof.   Here's your code, modified to do that:

Code:
set maybe {hi hello bye}
set shout {GDAY ELLO TARA}
 
while {1} \
{
   expect \
   {
  "\n"         {
       for {set k 0} {$k< [llength $maybe]} {incr k} {
               set lookfor [lindex $maybe $k]
               if {[string match *$lookfor* $expect_out(buffer)]} {
                       set wanted [lindex $shout $k]
                       Send_Output "$wanted\r"  
                       }
               }
       }
  timeout     {Time_Out; break}
  eof {puts "\rBreaking - EOF\r" ; break}
   }
}


The method is to have expect look for each new line, then to look for the appropriate pattern within each line.  If a line does not match at all, it is skipped. And I've generalised the code to allow easily for more than 3 interesting inputs ....

The results file I got:

1 ELLO
2 GDAY
3 TARA


Very good question, Neil ...





This page is a thread posted to the opentalk forum at www.opentalk.org.uk and archived here for reference. To jump to the archive index please follow this link.

You can Add a comment or ranking to this page

© WELL HOUSE CONSULTANTS LTD., 2014: Well House Manor • 48 Spa Road • Melksham, Wiltshire • United Kingdom • SN12 7NY
PH: 01144 1225 708225 • FAX: 01144 1225 899360 • EMAIL: info@wellho.net • WEB: http://www.wellho.net • SKYPE: wellho