LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - General
User Name
Password
Linux - General This Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.

Notices


Reply
  Search this Thread
Old 10-17-2006, 01:49 PM   #1
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Rep: Reputation: 0
Need help with BASH-scripts and EXPECT


Hi Guys,

I'm basically a router guy and know just enough linux to get by daily. I am trying to get a bash-script with Expect running that helps me to mass-send telnetcommands to 500 Cisco routers I manage.

What I want to do is:
I have 2 textfiles. One textfile with ip-addresses of the hosts I want to telnet to (called targets.txt) and one textfile that contains the commands (called commands.txt) that I want to send to each and every ip-address in targets.txt.

Example targets.txt looks like this:
10.0.0.1
10.0.0.2

Example commands.txt looks like this:
configure terminal
interface fa0/0
ip address 192.168.0.1 255.255.255.0
end
quit

I have a Expect-script that telnets to the router and logs in, but I have no idea how to solve the script that needs to read from two files to get the variables.

Now, I'm guessing I need some 'foreach ' statements in a bash-scripts or somthing. But this is were my linux-skills stops. Really need some help over here.

Appreciate any comments you might have!

Best Regards,
Fredrik
 
Old 10-17-2006, 08:21 PM   #2
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
There's no need to wrap up expect code in a bash script - expect provides functions for reading from files etc. Having just one program file is typically easier to maintain.

Here's a little expect code to get you started:

Code:
#!/usr/bin/env expect

set load_fh [open "ip_list_file" r]
set ip_list [split [read $load_fh] "\n"]
close $load_fh

foreach ip $ip_list {
    if {$ip != ""} {
        send_user "processing IP $ip\n"
    }
}
 
1 members found this post helpful.
Old 10-18-2006, 02:39 AM   #3
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Original Poster
Rep: Reputation: 0
Thumbs up

Hey Matthew,

Thanks! I think I understand, I'll try this tonight and let you know how it turned out.

Regards,
Fredrik
 
Old 10-18-2006, 03:32 AM   #4
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
This example doesn't include error checking, but we can discuss that later. Let's get the basics in place first.

A hint for the login process - expect runs other programs by calling the spawn procedure and then interacts with them using the send procedure and expect is used to scan the output of the called program for some pattern (and thus decide what to send to it). send_user is used to print output from the expect script.

Typically an automated login process might look something like this (assuming you've previously set up variables ip, username and password):

Code:
eval spawn "telnet $ip"
expect "^Login:"
send "$username\n"
expect "Password:"
send "$password\n" 
expect "prompt> "
send "configure terminal\n"
expect "prompt> "
send "interface fa0/0\n"
...
This is the simplest way to do expect ... send. You can also expect multiple things at once and perform an action based on which one is encountered. This is useful for logging in with ssh because ssh might thrown up one of many messages if the host hasn't been logged into before and so on:

Code:
expect {
    "Are you sure you want to 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
    }
    "$the_user's password: " {
        send "[get_pass]\n"
    }
}
Note that you probably DON'T want to automatically add new host keys with ssh - the warnings about changed keys should probably be handled by the user.

Another thing you can see here is a procedure call. The line:

Code:
        send "[get_pass]\n"
...is a procedure call. You use the square brackets to call fuctions, in this example called get_pass which presumably determines the correct password for the account.

The last thing about this example is exp_continue, which tells expect to stay in the current expect block after the current match.
 
Old 10-21-2006, 03:51 AM   #5
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Original Poster
Rep: Reputation: 0
Hey Matthew,

Just started to playing around with expect again. This is how far I've gotten and now I am stuck again. The script seems to run fine, but I cannot get the script to send the actual commands from the commands.txt. See the script below.

Code:
#!/usr/bin/expect --

# Usage
set timeout 5

set loginname [lindex $argv 0]
set vtypassword [lindex $argv 1]
set enablepassword [lindex $argv 2]

set load_fh [open "targets.txt" r]
set ip_list [split [read $load_fh] "\n"]
close $load_fh

foreach ip $ip_list {
        if {$ip != ""} {
                send_user "telnet to this host: $ip\n"

                # Connect
                spawn telnet $ip

                expect "sername:" {
                 send "$loginname\n"
                 sleep 0.5
                 expect "assword:"
                 send "$vtypassword\n"
                 sleep 0.5
                 expect ">" {
                  send "enable\n"
                  expect "assword:"
                  send "$enablepassword\n"
                  send "term length 0\n"
                 }
                 send "$vtypassword\n"
                 sleep 0.5
                 expect ">"
                 send "enable\n"
                 expect "assword:"
                 send "$enablepassword\n"
                 sleep 0.5
                 expect "#"
                 send "term length 0"
                } \
                "ogin:" {
                 send "$loginname\n"
                 sleep 0.5
                 expect "assword:"
                 send "$vtypassword\n"
                }

                set load_cmd [open "commands.txt" r]
                set cmd_list [split [read $load_cmd] "\n"]
                close $load_cmd

                foreach command $cmd_list {

                        if {$command != ""} {
                                send_user "%% begin send commands %%\n"
                                send "$command\n"
                                send_user "%% end send commands %%\n"
                                send "logout\n"
                        }
                }
        }
Now, the results are a bit confuing. This is what is happening.

Code:
./expectscripter admin "my secret password"

spawn telnet 1.1.1.1
Trying 1.1.1.1...
Connected to 1.1.1.1.
Escape character is '^]'.

**************************************************
*  Access only allowed for authorized personel!  *
*          All actions are logged!               *
**************************************************


User Access Verification

Username: admin
Password: 

router#%% begin send commands %%
%% end send commands %%
The script does not send the commands from the 'send "$command\n"' line. If I do a 'send_user "$command\n"' I can see that the command get echoed back to me.

Do you have any suggestions?

Thanks,
Fredrik
 
Old 10-21-2006, 04:31 AM   #6
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
It looks OK to me so long as the commands.txt contains something. [scratches head].

The only thing I can think of is that maybe the remote device (or rather the telnet protocol) expects something \r\n or \r instead of \n to tell it that you've pressed return. This only needs to be changed after login, so just change the
Code:
send "$command\n"
to
Code:
send "$command\r"
or
Code:
send "$command\r\n"
One more minor point, consider moving the read of the command.txt file to the top of the file, so you only have to read the file once.

Matthew
 
Old 10-21-2006, 09:24 AM   #7
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Original Poster
Rep: Reputation: 0
Alright. I'll give it a try.

Note, I had the procedure for read command.txt at the top before. I changed to see if there were any change. I'll let you know what happened.

Thanks,
Fredrik
 
Old 10-21-2006, 11:28 AM   #8
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Original Poster
Rep: Reputation: 0
Damn. It does not seem to work, doesn't matter what I do. Do you know if there's any limitation to having a foreach statement within another?

Regards,
Fredrik
 
Old 10-21-2006, 11:34 AM   #9
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
I'm pretty sure nested foreach's are OK. It's seems to be getting to the correct section because you can see the "%% begin send commands %%" message in the output.

There is a program called autoexpect with which you can "record" a session. It generates a script which you can then re-run at another time. The scripts it produces are a little ugly, but it would be a good way to see exactly what needs to get sent, and then you can modify your script as necessary.
 
Old 10-21-2006, 11:42 AM   #10
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
One more thought. Where you would send the command, use the "interact" command. This connects your terminal to the remote session as if you had logged in manually. Try to type in the commands manually to check that the login is OK.
 
Old 10-21-2006, 03:05 PM   #11
ElFredrico
LQ Newbie
 
Registered: Jul 2003
Posts: 6

Original Poster
Rep: Reputation: 0
If I substitute the "send command\n" with interact, everything works fine. I can interact with the router just fine.

Code:
[root@netserver02:/var/www/localhost/php]# ./expectscripter "admin" "secretpassword"
telnet to this host: 1.1.1.1
spawn telnet 1.1.1.1
Trying 1.1.1.1...
Connected to 1.1.1.1.
Escape character is '^]'.

**************************************************
*  Access only allowed for authorized personel!  *
*          All actions are logged!               *
**************************************************


User Access Verification

Username: admin
Password: 

Router#
Router#sh run int lo0      <<<this is me typing
Building configuration...

Current configuration : 69 bytes
!
interface Loopback0
 ip address 1.1.1.1 255.255.255.255
end

Router#quit
Connection closed by foreign host.
end processing host: 1.1.1.1
Bummer. If you want you can telnet set up my script to telnet into these two public route-servers on the internet:
route-server.cerf.net
route-server.ip.tiscali.net

and do the ios-command "show ip bgp 192.168.0.0/16" It will probably say that the network don't exists buts thats alright. Just to prove a point here.

Code:
#!/usr/bin/expect

set timeout 5

set loginname [lindex $argv 0]
set vtypassword [lindex $argv 1]
set enablepassword [lindex $argv 2]


set load_fh [open "targets.txt" r]
set ip_list [split [read $load_fh] "\n"]
close $load_fh

set load_cmd [open "commands.txt" r]
set cmd_list [split [read $load_cmd] "\n"]
close $load_cmd


foreach ip $ip_list {
        if {$ip != ""} {
                send_user "telnet to this host: $ip\n"

                # Connect
                spawn telnet $ip

                expect "sername:" {
                 sleep 0.5
                 send "$loginname\n"
                 expect "assword:"
                 sleep 0.5
                 send "$vtypassword\n"
                 set timeout 1
                 expect ">" {
                  sleep 0.1
                  send "enable\n"
                  expect "assword:"
                  sleep 0.5
                  send "$enablepassword\n"
                 }
                } \
                "assword:" {
                 send "$vtypassword\n"
                 expect ">"
                 send "enable\n"
                 expect "assword:"
                 send "$enablepassword\n"
                } \
                "ogin:" {
                 send "$loginname\n"
                 expect "assword:"
                 send "$vtypassword\n"
                }

                set timeout 5

                foreach command $cmd_list {
                        if {$command != ""} {
                                send_user "%% begin send commands %%\n"
                                send -- "command\n"
#                               interact
                                send_user "%% end send commands %%\n"
                        }
                }
                send_user "end processing host: $ip\n\n"
        }
}

Anything?

Last edited by ElFredrico; 10-21-2006 at 03:07 PM.
 
Old 10-22-2006, 05:49 AM   #12
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
OK, I re-wrote the script a little and it works. I think (but I'm not certain) that the problem was with some expect statements not finishing before you sent your commands.

commands.txt (my example):
Code:
help
show ip bgp 192.168.0.0/16
targets.txt:
Code:
route-server.cerf.net
route-server.ip.tiscali.net
test script:
Code:
#!/usr/bin/expect

set timeout 5

set load_fh [open "targets.txt" r]
set ip_list [split [read $load_fh] "\n"]
close $load_fh

set load_cmd [open "commands.txt" r]
set cmd_list [split [read $load_cmd] "\n"]
close $load_cmd

send_user "Starting up now..."

foreach ip $ip_list {
    if {$ip != ""} {
        send_user "connecting to: $ip...\n"

        # Connect
        spawn telnet $ip
        set prompt "route-server.*>"

        foreach command $cmd_list {
            if {$command != ""} {
                expect -re $prompt {
                    send_user "\nfound prompt, sending command: \"$command\"\n"
                    send "$command\r"
                }
            }
        }

        expect -re $prompt {
            send_user "\nfound prompt, sending command: \"exit\"\n"
            send -- "exit\r"
        }
                
        send_user "I'm done with $ip\n"
        close
    }
}

send_user "all done\n"
The real trick here is choosing a good value for "prompt". This is a regular expression which must match the prompt. The thing is, if you match something in the device's login message (before the actual prompt), it'll send the command too soon, and that may not work.

If the devices to which you wish to connect are consistent in how they display the prompt, this won't be too hard. However, I notice the two examples you gave me had different ways of constructing the prompt. The first just says "route-server>", but the second one says "route-server.ip.tiscali.net>". Fortunately the regular expression "route-server.*>" matches both, but this may not apply to all your devices... It'll be a matter of research on your part to see what all the different devices do and make some code to handle it.

Hope that helps
 
Old 01-18-2011, 03:04 PM   #13
robhumes
LQ Newbie
 
Registered: Jan 2011
Posts: 2

Rep: Reputation: 0
Expect script modification

Matthew, Thanks for the script. I've been tasked to test connectivty to a our customers routers and switches as part of the onboarding process. One problem I've run into has been the differences in account access. Some devices are accessed via telnet, some via SSH. These devices may also have different usernames and passwords.

I'd like to be able to import more than just the Host to connect to. Maybe a .csv file that contains:
AccessMethod Hostname Username Password
Telnet 172.1.1.1 Cisco Ci$c0123
SSH 172.1.1.2 Cisco Ci$c0123

The script would then spawn the correct access based on what's read from AccessMethod.
The output would need to be written to a file for review.
Any help would be greatly appreciated. thanks
 
Old 01-18-2011, 03:15 PM   #14
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
I'll write you a script if you pay me. :-)
 
Old 02-03-2011, 11:57 AM   #15
robhumes
LQ Newbie
 
Registered: Jan 2011
Posts: 2

Rep: Reputation: 0
I'm interested in having you create a script.
 
  


Reply

Tags
bash, expect, script



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
expect/bash sk8guitar Programming 5 10-06-2009 12:00 PM
Trouble calling 'expect' from bash raypen Linux - Software 1 06-01-2006 08:05 PM
expect scripts and cron banderson Linux - Software 8 05-27-2005 04:44 AM
using expect scripts for remote admin jmr71769 Linux - General 3 01-21-2004 01:27 PM
Bash scripts? BajaNick Programming 3 07-05-2003 10:13 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - General

All times are GMT -5. The time now is 11:20 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration