Skip to main content

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.
1
2
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#! /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
1
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#! /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
1
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.

Comments

Popular posts from this blog

Interpreting the output of lspci

On Linux, the lspci command lists all PCI devices connected to a host (a computer). Modern computers and PCI devices communicate with each other via PCI Express buses instead of the older Conventional PCI and PCI-X buses since the former buses offer many advantages such as higher throughput rates, smaller physical footprint and native hot plugging functionality. The high performance of the PCI Express bus has also led it to take over the role of other buses such as AGP ; it is also expected that SATA buses too will be replaced by PCI Express buses in the future as solid-state drives become faster and therefore demand higher throughputs from the bus they are attached to (see this article for more on this topic). As a first step, open a terminal and run lspci without any flags (note: lspci may show more information if executed with root privileges): lspci   This is the output I get on my laptop: 00:00.0 Host bridge: Intel Corporation Haswell-ULT DRA...

Boot process hangs at dracut: Switching root

Environment Red Hat Enterprise Linux 6 Issue When server is booting the boot process hangs at  dracut: Switching root , and never displays anything else. Raw device-mapper: ioctl: 4.33.1-ioctl (2015-8-18) initialised: xx-xxxx@redhat.com udev: starting version 147 dracut: Starting plymouth daemon dracut: rd_NO_DM: removing DM RAID activation dracut: rd_NO_MD: removing MD RAID activation scsi0 : ata_piix scsi1 : ata_piix ata1: PATA max MWDMA2 cmd 0x1f0 ctl 0x3f6 bmdma 0xc120 irq 14 ata2: PATA max MWDMA2 cmd 0x170 ctl 0x376 bmdma 0xc128 irq 15 Refined TSC clocksource calibration: 2599.999 MHz. virtio-pci 0000:00:03.0: PCI INT A -> Link[LNKC] -> GSI 11 (level, high) -> IRQ 11 virtio-pci 0000:00:05.0: PCI INT A -> Link[LNKA] -> GSI 10 (level, high) -> IRQ 10 virtio-pci 0000:00:07.0: PCI INT A -> Link[LNKC] -> GSI 11 (level, high) -> IRQ 11 virtio-pci 0000:00:08.0: PCI INT A -> Link[LNKD] -> GSI 11 (level, high) -> IRQ 11 input: ImExPS/2 G...

How to Remove a Storage Device (LUN)

Before removing access to the storage device itself, it is advisable to back up data from the device first. Afterwards, flush I/O and remove all operating system references to the device. Stop all access to the device that has to be removed. Unmount the device. Remove the device from any md and LVM volume that is using it. If a multipath device is being removed, run  multipath -l  and take note of all the paths to the device. When this has been done, remove the multipath device: # multipath -f device   Use the following command to flush any outstanding I/O to all paths to the device: # blockdev –flushbufs device   Remove any reference to the device's path-based name, like  /dev/sd  or  /dev/disk/by-path  or the major:minor number, in applications, scripts, or utilities on the system. This is important to ensure that a different device, when added in the future, will not be mistaken for the current device. The fi...