3

I'm pretty new to scripting in general. I'm writing an expect script that ssh'es into a Cisco switch, and runs the "show cdp neighbors" command to get a list of all the devices connected to the switch. I then save the output into a variable and exit the ssh session. I have the username and password being set in the included file.

#!/usr/bin/expect -f
#exp_internal 1

source accountfile
set timeout 10
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME@<hostname>\r"
expect {
  "continue connecting" {
    send_user "Adding host to ssh known hosts list...\n"
    send "yes\n"
    exp_continue
  }
  "Do you want to change the host key on disk" {
    send_user "Changing host key on disk...\n"
    send "yes\n"
    exp_continue
  }
  "assword:" {
    send "$PASSWORD\r"
  }
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
expect "#"
set result $expect_out(buffer)
send "exit\r"
expect "#"

So then I want to take $result and look for lines that contain ' R ', and save those lines to a file (R with spaces on either side indicates a router, which is what I'm interested in)

The problem is that if the name of a connected device is long, it puts the name of the device on one line, and then the rest of the data about the device on the next line. So if I match the ' R ' string, I won't get the name of the device, since the name is on the previous line.

Device ID        Local Intrfce     Holdtme     Capability Platform  Port ID
...
<device_name_really_long>
                 Gig 2/0/52        171             R S I  WS-C6509  Gig 3/14
<device_name2>   Gig 2/0/1         131             H P M  IP Phone  Port 1
...

Any ideas? there's probably a regex that would do it, but I don't know squat about regex.

SOLVED: thanks to Glenn Jackman

I ended up having to add an expect condition to check if I had a full buffer, so my final code looks like this:

#!/usr/bin/expect
#exp_internal 1
match_max 10000
set expect_out(buffer) {}
set timeout 30

source accountfile
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME@ol2110-3750stack.sw.network.local\r"
expect {
    "continue connecting" {
        send_user "Adding host to ssh known hosts list...\n"
        send "yes\n"
        exp_continue
    }
    "Do you want to change the host key on disk" {
        send_user "Changing host key on disk...\n"
        send "yes\n"
        exp_continue
    }
    "assword:" {
        send "$PASSWORD\r"
    }
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
set result ""
expect {
    {full_buffer} {
        puts "====== FULL BUFFER ======"
        append result $expect_out(buffer)
        exp_continue
    }
    "#" {
        append result $expect_out(buffer)
    }
}
send "exit\r"
expect "#"
set devices [list]
set current_device ""
set lines [split $result "\n"]
foreach line $lines {
    set line [string trim $line]
    if {[llength $line] == 1} {
        set current_device $line
        continue
    }
    set line "$current_device$line\n"
    if {[string match {* R *} $line]} {
        lappend devices $line
    }
    set current_device ""
}

puts $devices
thepoynt
  • 43
  • 1
  • 6

1 Answers1

0
set devices [list]
set current_device ""
foreach line [split $result \n] {
    if {[llength [split [string trim $line]]] == 1} {
        set current_device $line
        continue
    }
    set line "$current_device$line"
    if {[string match {* R *} $line]} {
        lappend devices $line
    }
    set current_device ""
}

# devices list should now contain "joined" routers.
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • When I do that and then puts $devices, this is what I get: – thepoynt Nov 16 '12 at 15:08
  • } Gig 2/0/52 174 R S I WS-C6509 Gig 3/14 – thepoynt Nov 16 '12 at 15:09
  • In other words, I'm only getting one line (I should have about 8), and that is just the second line with a closing bracket at the front. That would make it seem there's a mismatched bracket somewhere, but I don't see one... – thepoynt Nov 16 '12 at 16:05
  • Does the device name have spaces? – glenn jackman Nov 16 '12 at 16:31
  • OK, splitting the lines with \r instead of \n, and adding "set line [string trim $line]" as the first line in the first if statement and before the set line "$current..." seems to do the trick (kind of). I now have a list with the "unjoined" router that have been "joined", but only those routers. The routers that start out all on one line aren't put in the list. – thepoynt Nov 16 '12 at 16:34
  • No, they shouldn't have spaces. – thepoynt Nov 16 '12 at 16:35
  • I guess the \n and \r don't make a difference, it works either way – thepoynt Nov 16 '12 at 16:38
  • you're not providing anything in your comments that give me any more clues. Please provide some sample input and your current parsing code. Also the code you've shown in hour question does jot relate to the problem at hand, so you might as well remove it. – glenn jackman Nov 16 '12 at 19:09
  • I finally figured out what my problem is - expect_out(buffer) is not giving me the entire output of the show cdp neighbors command. It only gives me the last 2/3 or so, starting in the middle of a line. I've tried setting match_max 10000 at the beginning of the script, but with no luck. – thepoynt Nov 19 '12 at 16:06