From: Darren Millin on 6 Feb 2005 19:45 I have written a large expect script, which is starting to become a bit unwieldly. Does anyone have any advice on how best to structure large expect scripts. Thanks, Darren
From: Bryan Oakley on 6 Feb 2005 20:32 Darren Millin wrote: > I have written a large expect script, which is starting to become a bit > unwieldly. Does anyone have any advice on how best to structure large > expect scripts. > > Thanks, > > Darren Divide functionality up and put into separate files; use a makefile to combine the parts into an application.
From: Stefan Finzel on 7 Feb 2005 02:56 There may be several reasons causing your code to get to unwieldly. Many of us start using expect in a linear way. But often it is much simpler to maintain the code by splitting it to several files and switching to an "endlichen Automaten" (sorry don't know the englisch term) like this snipped example proc nortelnetworks:handler {cmd addr timeout login password cmds} { # 1st case: handle accelar/passport # 2nd case: handle baystack: ... press <Return> or <Enter> to select # ... set lines {} if {[catch {exp_spawn ${cmd} ${addr}} err]} { return ${err} } expect { -nocase -re {[- /a-z]*(incorrect|invalid).*$} { set lines $expect_out(0,string) } -nocase -re "enter ctrl-y to begin.*$" { exp_send -- "" exp_continue } -nocase -re {([^#>](#|>|\]:)| to select .*) *$} { foreach cmd ${cmds} { if {[regexp -- {^\[(.+)\]$} ${cmd} UNUSED cmd]} { # callback mechanismen set lines [${cmd} $expect_out(spawn_id) ${lines}] } else { # ... } } catch {exp_send -s -- "quit\r"} } -nocase -re "(login|username):.*$" { if {[llength ${login}]} { exp_send -s -- "${login}\r" set login {} exp_continue } else { set lines {login password looping} } } -nocase -re {passwor[dt]:.*$} { if {[llength ${password}]} { exp_send -s -- "${password}\r" set password {} exp_continue } else { set lines {login password looping} } exp_continue } ... {} eof { set lines eof } timeout { catch {exp_send -s -- "quit\r"} set lines timeout } } catch { exp_close exp_wait } # ... return ${lines} } Now different tasks for both command and menu driven devices can be handled very simple by one loop. Using seperated handlers, allows to focus on the main task as this example of saving a configuration by tftp demonstrates # ... nortelnetworks:handler telnet 10.10.10.10 5 mylogin mypassword { G C {"[TFTP:file ${addr} run-cfg]"} {"\t"} {"${::tftp_addr}"} {"\t"} { } {[nortelnetworks:sgConfigTFTP:baystack:callback]} {"\x03L"} } # ... This is additionally using a callback mechanismen {"[...]"} to add arbitrary procs. This can be used to insert functionality to the main loop too. proc nortelnetworks:sgConfigTFTP:baystack:callback {id lines} { # start and wait for end of transfer operation set res ${lines} # beware of ordering expect { -i ${id} -nocase -re { no .*$} { append res $expect_out(buffer) } -i ${id} -nocase -re { yes .*$} { append res $expect_out(buffer) exp_continue } -i ${id} eof { append res "\neof\n" } -i ${id} timeout { append res "\ntimeout\n" } } return ${res} } Seperating to different files is now very easy. Use source to recombine as needed. That was my way to do it and it reduced the code base to less than a half although doubling the tasks at rewrite. Darren Millin wrote: > I have written a large expect script, which is starting to become a bit > unwieldly. Does anyone have any advice on how best to structure large > expect scripts. > > Thanks, > > Darren
From: Cameron Laird on 7 Feb 2005 10:08 In article <kyzNd.37987$iC4.10230(a)newssvr30.news.prodigy.com>, Bryan Oakley <oakley(a)bardo.clearlight.com> wrote: >Darren Millin wrote: >> I have written a large expect script, which is starting to become a bit >> unwieldly. Does anyone have any advice on how best to structure large >> expect scripts. >> >> Thanks, >> >> Darren > >Divide functionality up and put into separate files; use a makefile to >combine the parts into an application. ? Bryan, I'm confused; while I certainly involve plenty of Makefiles in my Tcl-based work, I don't see it as pertinent to the advice at hand. As I read your response, you're suggesting transformation of big_monolithic_source.tcl into main.tcl case1.tcl input_output.tcl other_stuff.tcl ... where main.tcl might look like source case1.tcl source input_output.tcl source other_stuff.tcl ... main (let's neglect [namespace], [package], and other secondary opportunities for now). Is that what you're saying? How does a Makefile help with that?
From: Bryan Oakley on 7 Feb 2005 10:28
Cameron Laird wrote: > ? Bryan, I'm confused; while I certainly involve plenty of Makefiles > in my Tcl-based work, I don't see it as pertinent to the advice at > hand. As I read your response, you're suggesting transformation of > > big_monolithic_source.tcl > > into > > main.tcl > case1.tcl > input_output.tcl > other_stuff.tcl > ... > > where main.tcl might look like > > source case1.tcl > source input_output.tcl > source other_stuff.tcl > ... > > main > No. The way I use makefiles is something like this: foo: main.tcl casel.tcl input_output.tcl other_stuff.tcl cat main.tcl casel.tcl input_output.tcl other_stuff.tcl # this assumes main.tcl defines the proc "main" echo "main" >> foo I pretty much never use the source command except when debugging. |