Using Expect script to automate SSH logins and do routine tasks accross multiple hosts

If you maintain several Linux hosts sometimes it is easier if you can automate logins and do routine tasks unattended. In this blog we will show you how to accomplish this using Expect scripts.

Expect is available in most Linux distributions. If it is not already installed in your system you install it in Debian as shown below.

apt-get update
apt-get install expect

Here is a working expect script that reads all inputs from command line. I will break it down as we go on. Please note that lines beginning with ;# are comments.

#! /bin/expect


set timeout 20

;# -- command line arguments to our scripts
set user [lindex $argv 0]
set password [lindex $argv 1]
set host [lindex $argv 2]

;# shell prompt character
set prompt "$ "

;# -- main procecure 
proc dostuff {} {
   send -- "ls -lrt\r"
   return
}

;# script start running here

spawn /usr/bin/ssh $user@$host

;# loops forever until we get a shell prompt

while (1) {

   expect {
     ;# -- This is the prompt when you first use
     ;# -- ssh that says "Are you sure you want to continue ..."

     "no)? " {
        send -- "yes\r"
     }

     ;# -- the prompt for password
     "password: " {
         send -- "$password\r"
     }

     ;# -- and finally we got a shell prompt
     "$prompt" {
        dostuff
        break
     }
   }

}

;# -- exit
expect "$prompt"
send -- "exit\r"

expect eof

The script is executed as below

expect {filename-of-the-script} user password host

The script starts by executing ssh through spawn command. We go into a while loop until we get a shell prompt which signals that we have successfully logged in. We then call our main task the procedure dostuff. Our expect statement takes a switch form which executes a case that matches part of the ssh program prompt presented to our script.

The "--" after the send statement means that its parameter should be treated as plain string. A send statement is terminated with a "\r" charater simulating a carraige return. The shell prompt character that we use is the $ sign. You may want to change that if you have different shell setup.

Below is the same script but now it reads hosts information from a text file. Entries to the text file is teminated by a return character. We are assuming that all hosts has a user with password that you specify from command line.

#! /bin/expect

set timeout 20
set user [lindex $argv 0]
set password [lindex $argv 1]
set prompt "$ "

;# -- main activity

proc dostuff { currenthost} {

   ;# do something with currenthost

   send -- "ls -lrt\r"
   return
}

;# -- start of task

set fd [open ./hostlist r]
set hosts [read -nonewline $fd]
close $fd


foreach host [split $hosts "\n" ] {

    spawn /usr/bin/ssh $user@$host

    while (1) {
        expect {

        "no)? " {
           send -- "yes\r"
        }
       
        "password: " {
            send -- "$password\r"
        }
        
        "$prompt" {
           dostuff { $host }
           break
        }
      }
   }

   expect "$prompt"
   send -- "exit\r"

}

expect eof

The script is now executed as below

expect {filename-of-the-script} user password

Our text is read into a buffer which is then split into a list in our for loop. Notice that our hostlist file should be in the folder as our script. Also notice that our dostuff procedure now takes a parameter. You may want to use this parameter to select a task you may require for that specific host.

That's it. Good luck.

If you like the article, please share.
(Site URL pattern has changed as a result social actions counter was reset.)



Comment icon Comments (Newest first)