PrakashG
PrakashG

Reputation: 1642

How to use expect in Shell script like nested If Else loop?

I have written below Shell script which is intended to get Model name from remote host by doing SSH and executing the command.

#!/bin/bash

> output.csv

IFS=","

echo "IP,Model Name" >> output.csv

while read ip
do
    #echo "Current IP is: $ip"      

    model=expect -c 'spawn ssh username@'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r";  expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco

    echo "$ip,$model" >> output.csv

done < Check_SSH.csv

When I execute below command manually, then it gives expected model name as output.

Command: expect -c 'spawn ssh username@'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r"; expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco

But when its put into script like above it doesn't produce any output.

Also, there are MOTD (Message of the day) configured on most of the servers and "The authenticity of host..." message to adding server into .ssh/known_hosts, So I tried to handle them in script but Expect is failing to handle the situation when MOTD doesn't appear or when remote is already present in .ssh/known_hosts.

Any help is highly appreciated to get this script running.

Expected output:

IP,Model Name
8.8.8.8,C9407R
8.8.8.1,C9407R
8.8.8.2,C9407R
8.8.8.3,C9407R

Upvotes: 0

Views: 785

Answers (1)

glenn jackman
glenn jackman

Reputation: 247062

First, you're missing the Command Substitution syntax to execute the expect code:

model=$(expect -c ...)
# ....^^.............^

Next, to optionally expect patterns, you need the expect {patt1 action1 patt2 action2 ...} form:

expect -c '
    spawn ssh username@'"$ip"' "show version | in cisco"
    expect {
        -re "The.*(yes/no)?"      {send "yes\r"; exp_continue}
        -re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
        eof
    }
'

That way, expect can match any of the patterns. The exp_continue command "loops" within the same expect command so you can match more than one of them. The eof pattern matches when ssh connection closes after the "show version ..." command has finished.

Newlines for readability.

Putting this together:

model=$(
    expect -c '
        spawn ssh username@'"$ip"' "show version | in cisco"
        expect {
            -re "The.*(yes/no)?"      {send "yes\r"; exp_continue}
            -re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
            eof
        }
    ' | grep -i cisco
)

I have a feeling that there's more you need to do in the grep part, but you didn't show the output of just the expect command.


update:

  1. use spawn -noecho ssh ... so expect will not print the spawn command.
  2. then, you'll get whatever output ssh needs to show for the login process, and then the "show" command output:
    • if you're expecting exactly 1 line of output, you might want to change grep to tail -n 1.
    • otherwise, show the output you get and we can help you filter out the noise.

update 2: filtering out the noise

I'm going to assume that the regex pattern cisco (.*) processor is what you need to match:

model=$(
    expect -c '
        log_user 0
        spawn ssh username@'"$ip"' "show version | in cisco"
        expect {
            -re "The.*(yes/no)?"       {send "yes\r"; exp_continue}
            -re ".*UNAUTH.*password:"  {send "password\r"; exp_continue}
            -re "cisco (.*) processor" {puts $expect_out(1,string)}
        }
        expect eof
    '
)

log_user 0 turns off the spawned process's ability to write to stdout. Expect can still capture its output though.

Upvotes: 2

Related Questions