Search code examples
bashautomationexpectcisco

Mixing Expect and BASH


I have written a small Expect script to log into a Cisco device; once logged in I want to repeatedly run a command and grep the output.

#!/usr/bin/expect

send_user "Device name: "
expect_user -re "(.*)\n"
set host $expect_out(1,string)

send_user "Username: "
expect_user -re "(.*)\n"
set user $expect_out(1,string)

stty -echo
send_user -- "Password: "
expect_user -re "(.*)\n"
set pass $expect_out(1,string)
stty echo

send_user "show int "
expect_user  -re "(.*)\n"
set intf $expect_out(1,string)
send_user "\n"

spawn telnet $host
expect "Username:"
send "$user\r"
expect "Password:"
send "$pass\r"
expect ">"

At this point we have logged into the device, I want to execute the command "show int xxx" repeatedly and grep the output for a specific line. grep isn't in Expect, nor a command like sleep, so I can loop round executing the show int command, grepping out my specific line. How can I mix Expect and Bash like this?

UPDATE: I've pretty much done the script now, I'll post the full script once I get over this last hurdle. A line set bytesnow [exec grep "packets input" \< showint | cut -d \ -f 9] is throwing the error;

child process exited abnormally
   while executing
"exec grep "packets input" < \showint | cut -d \  -f 9"

But it works fine in a test script I wrote. The file ./showint is there, running that command on the command line works fine? I can't work out what's wrong?

UPDATE: More investigation (http://wiki.tcl.tk/8489) has shown me that the grep exits with status code 1, which means no pattern matches were found, put the command works just fine from the command line? Even with /full/path/to/showint.

END: I fixed my mistake by realising what a fool I had been, answered below. Thanks all for your help :D


Solution

  • This is my first Expect script, its purpose is to give the live (almost, 1 second!) throughput of an interface. The below example gives an interface input speed, because we grep for the line containing "packets input". Change this to "packets output" to get a live output rate for that interface.

    #!/usr/bin/expect
    
    # Long delay for those tricky hostnames
    
    set timeout 60
    
    # Prompt user for device name/IP, username, password, 
    # and interface to query (gi0/2)
    
    send_user "Device name: "
    expect_user -re "(.*)\n"
    set host $expect_out(1,string)
    
    send_user "Username: "
    expect_user -re "(.*)\n"
    set user $expect_out(1,string)
    
    stty -echo
    send_user "Password: "
    expect_user -re "(.*)\n"
    set pass $expect_out(1,string)
    send_user "\n"
    stty echo
    
    send_user "show int "
    expect_user  -re "(.*)\n"
    set intf $expect_out(1,string)
    send_user "\n"
    
    spawn telnet $host
    expect "Username:"
    send "$user\r"
    expect "Password:"
    send "$pass\r"
    expect ">"
    
    set byteslast 0
    set bytesnow 0
    
    log_user 0
    
    # Enter a continuous loop grabbing the number of bytes that
    # have passed through an interface, each second.
    # The different in this number each cycle, is essentially
    # how much traffic this interface is pushing.
    
    while { true } {
      send "show int $intf\r"
      expect ">"
    
      set showint [open "showint" "w"]
      puts $showint $expect_out(buffer)
      close $showint
    
      set bytesnow [exec grep "packets input" \< showint | cut -d \  -f 9]
    
      if { $bytesnow > $byteslast } {
        set diff [expr $bytesnow - $byteslast]
        set bps [exec expr "$diff" \* 8]
        set kbps [exec expr "$bps" \/ 1000]
      } elseif { $bytesnow < $byteslast } {
        set diff [expr $byteslast - $bytesnow]
        set bps [exec expr "$diff" \* 8]
        set kbps [exec expr "$bps" \/ 1000]
      } elseif { $bytesnow == $byteslast } {
        set kbps 0
      }
    
      set byteslast $bytesnow
      puts "$kbps Kbps\r"
    
      sleep 1
    }
    

    As this is my first Expect script, I have no doubt it could be written more efficiently and clearly (that always the case I find), so if anyone has any pointers on this one I'm all ears! :)

    My problem with my exec grep command turned out to be that prior to that, the file I had opened "showint", I hadn't closed, and I was trying to access another file; school boy mistake!