From: Darren Millin on
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
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
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
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
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.

 |  Next  |  Last
Pages: 1 2 3
Next: Newbie Tcl/Tk build problem