Michael Cox
Michael Cox

Reputation: 1

Expect while loop dies after 29th iteration

I'm using an Expect script within Cygwin. It reads two input files: one is a list of network devices by full hostname, and the other is a list of commands to run on those devices while logging the output. It works until completing the 29th device. When the spawn command executes on the 30th device, I get this output:

send: spawn id exp65 not open
    while executing
"send -s "MyUserID\r""
    ("while" body line 30)
    invoked from within
"while {[gets $switches host] >= 0} {
        set hostname [string trimright $host]   ;# get rid of trailing whitespace
        if {[string length $hostname] == 0} {..."
    (file "./getna-lab.exp" line 37)

To rule out an issue with Cygwin, I wanted to test with my Mac where I normally used Expect, but I can't anymore (devices are secured and only available via Windows virtual desktops, hence Cygwin.) At first I thought it was because the "exit" from the telnet session wasn't working and they were remaining open, but that's not it; I tried adding "exit" to the command list file where it definitely executes.

The script and other file contents are listed below. Thanks for any assistance.

#!/usr/bin/expect

set timeout 600

log_user 0
match_max 100000
set expect_out(buffer) {}

set switchlist [lindex $argv 0]
set commandlist [lindex $argv 1]

#
# Open switch list file
#
if [catch {open "$switchlist" "r"} switches] {
    puts stderr "$CR$switches$CE\n"
    exit 1
}

#
# Verify command list file opens OK before we logon, etc.
#
if [catch {open "$commandlist" "r"} commands] {
    puts stderr "$CR$commands$CE\n"
    exit 1
}
close $commands

#
# Enable trace logging
#
trace variable expect_out(buffer) w log_by_tracing

#
# loop for each device in switch list, looping for each command in command list
#
while {[gets $switches host] >= 0} {
    set hostname [string trimright $host]   ;# get rid of trailing whitespace
    if {[string length $hostname] == 0} {   ;# leave loop if blank line detected
        break
        }
    regexp {^([^\..]+)(\..*$)} $hostname domain hostshort ;# peel off domain name
    send_user "\n$hostname:\nWorking...\n"
    if [catch {open "$commandlist" "r"} commands] {
        puts stderr "$CR$commands$CE\n"
        exit 1
    }
    log_file -noappend -a "$hostshort.txt"

        spawn telnet $hostname
        expect \
            {
            "sername:" {}
            "ogin:" {}
            "assword:" {send -s "\r" 
                            expect \
                                {
                                "ogin:" {}
                                "sername:" {}
                                }
                }
            timeout {
                send_user "Timed out on device $hostshort logon.\n"
                break
            }
        }

        send -s "myUserID\r"
        expect {
            "assword:" {}
            timeout {
                send_user "Timed out on device $hostshort logon.\n"
                break
            }
        }

        send -s "MyPassword\r"
        expect {
            "$hostshort" {}
            timeout {
                send_user "Timed out on device $hostshort logon.\n"
                break
            }
        }

        while {[gets $commands command] >= 0} {
            send -s "$command\r"
            expect {
                "$hostshort#" {}
                timeout {send_user "Timed out waiting for $hostshort # prompt.\n"}
            } 
        }
    send -s "exit\r"
log_file
}

close $switches
close $commands

I typically use a switchlist.txt file that looks something like this:

switch1.domainname.com
switch2.domainname.com

I also use a file called commands.txt that lists the commands like this:

show interface status
sh run

The script is executed via

# ./get-stuff.exp switchlist.txt commands.txt

Upvotes: 0

Views: 239

Answers (1)

glenn jackman
glenn jackman

Reputation: 247072

I suspect you may be running out of (something) by not closing the spawned processes properly inside that loop. Try replacing the contents of the loop with this:

spawn telnet $hostname
expect {
    "sername:" { send -s "$myUserId\r"; exp_continue }
    "assword:" { send -s "MyPassword\r"; exp_continue }
    timeout {
        send_user "Timed out on device $hostshort logon.\n"
        continue   ;# next host
    }
    "$hostshort"
}

while {[gets $commands command] >= 0} {
    send -s "$command\r"
    expect {
        "$hostshort#" {}
        timeout {
            send_user "Timed out waiting for $hostshort # prompt.\n"
            continue   ;# next host
        }
    } 
}
send -s "exit\r"
expect eof
wait 
exp_close

Upvotes: 1

Related Questions