0
votes

I have expect (tcl) script for automated task working properly - configuring network devices via telnet/ssh. Most of the cases there is 1,2 or 3 command lines to execute, BUT now I have more then 100 command lines to send via expect. How can I achieved this in smart and good scripting way :) Because I can join all command lines over 100 to a variable "commandAll" with "\n" and "send" them one after another, but I think it's pretty ugly :) Is there a way without stacking them together to be readable in code or external file ?

#!/usr/bin/expect -f
set timeout 20
set ip_address "[lrange $argv 0 0]"
set hostname "[lrange $argv 1 1]"
set is_ok ""

# Commands
set command1 "configure snmp info 1"
set command2 "configure ntp info 2"
set command3 "configure cdp info 3"
#... more then 100 dif commands like this !
#... more then 100 dif commands like this !
#... more then 100 dif commands like this !

spawn telnet $ip_address

# login & Password & Get enable prompt
#-- snnipped--#

# Commands execution

# command1
expect "$enableprompt" { send "$command1\r# endCmd1\r" ; set is_ok "command1" }
    if {$is_ok != "command1"} {
        send_user "\n@@@ 9 Exit before executing command1\n" ; exit
    }

# command2
expect "#endCmd1" { send "$command2\r# endCmd2\r" ; set is_ok "command2" }
    if {$is_ok != "command2"} {
        send_user "\n@@@ 9 Exit before executing command2\n" ; exit
    }

# command3
expect "#endCmd2" { send "$command3\r\r\r# endCmd3\r" ; set is_ok "command3" }
    if {$is_ok != "command3"} {
        send_user "\n@@@ 9 Exit before executing command3\n" ; exit
    }

p.s. I'm using one approach for cheeking is given cmd line is executed successfully but I'm not certain that is perfect way :D

2

2 Answers

1
votes

don't use numbered variables, use a list

set commands {
    "configure snmp info 1"
    "configure ntp info 2"
    "configure cdp info 3"
    ...
}

If the commands are already in a file, you can read them into a list:

set fh [open commands.file]
set commands [split [read $fh] \n]
close $fh

Then, iterate over them:

expect $prompt

set n 0
foreach cmd $commands {
    send "$cmd\r"
    expect {
        "some error string" {
            send_user "command failed: ($n) $cmd"
            exit 1
        }
        timeout {
            send_user "command timed out: ($n) $cmd"
            exit 1
        }
        $prompt 
    }
    incr n
}
0
votes

While yes, you can send long sequences of commands that way, it's usually a bad idea as it makes the overall script very brittle; if anything unexpected happens, the script just keeps on forcing the rest of the script over. Instead, it is better to have a sequence of sends interspersed with expects to check that what you've sent has been accepted. The only real case for sending a very long string over is when you're creating a function or file on the other side that will act as a subprogram that you call; in that case, there's no really meaningful place to stop and check for a prompt half way. But that's the exception.

Note that you can expect two things at once; that's often very helpful as it lets you check for errors directly. I mention this because it is a technique often neglected, yet it allows you to make your script far more robust.

...
send "please run step 41\r"
expect {
    -re {error: (.*)} {
        puts stderr "a problem happened: $expect_out(1,string)"
        exit 1
    }
    "# " {
        # Got the prompt; continue with the next step below
    }
}
send "please run step 42\n"
...