2

Background: I have to copy a file from one server, to over 100 servers in a test environment. once the file is copied, it requires to have the permissions on the file changed/verified. These are all linux servers. most of them have the same password for login, but some may not.

I need help with making a loop in my bash script. It is actually calling on Expect. The area I want to make 'better' or fix, is a few things

  • the part where it expects a password. (the * section), it should quit the script after 1 failed password attempt, and dump the ip to a text file called "fail.txt"
  • ideally there should be a piece that also dumps the ip to a fail.txt if there is no connection to the server.

I tried to write the password piece thinking its a loop, but I'm not really sure if that method of thinking is deal. I tried just adding another "expect Password: " thinking that if it gets the prompt a second time, to exit out, but I had a hard time with getting that to work. Thank you!

#!/bin/bash
while read ip; do

sleep 2
expect <<- DONE
        set timeout 1
        spawn scp yoman.txt root@$ip:/felixtemp
                if above command fails, dump the IP to fail.txt, otherwise continue
        expect yes/no { send yes\r }
        expect Password: { send aaaaaa\r } #if this is good, continue the script from *****
                else                                     #exit the script
                expect Password: { send 033\r }
                expect # { send "echo 'password failed'\r" }
                && dump to a text file called fail.txt
*****   expect # { send "exit\r\r" }
        sleep 1

        set timeout 1
        spawn ssh root@$ip
        sleep 2
        expect yes/no { send yes\r }
        sleep 2
        expect Password: { send aaaaaa\r }
        sleep 5
        expect # { send "cd /felixtemp\r" }
        expect # { send "chown informix:informix yoman.txt\r" }
        expect # { send "chmod 775 yoman.txt\r" }
        expect # { send "sum yoman.txt | grep 10350 && echo 'transfer good' || echo 'transfer bad'\r" }
        expect # { send exit\r }
        sleep 1
DONE

done < ip.txt
2
  • also, I cant use ssh keys in case someone suggest that, I have to copy a single file, out to over 100 servers, they are all linux boxes. Apr 20, 2015 at 18:09
  • i can't use sshpass cuz these boxes don't have internet access for installing packages. lame i know. Apr 22, 2015 at 17:50

3 Answers 3

1

If you have to manage hundreds of linux servers you should use a configuration management tool in order to execute these tasks. A very simple configuration management tool is ansible, the only requirement for managed system is python 2.4 or more( http://docs.ansible.com/intro_installation.html#managed-node-requirements ).

Your problem solved with ansible:

1) Define a list of hosts you can define a default password for all the hosts and an other password for some hosts

[hosts_list]
172.17.0.101 ansible_ssh_user=root ansible_ssh_pass=password
172.17.0.102 ansible_ssh_user=root ansible_ssh_pass=oldpassword
172.17.0.103
172.17.0.104

[hosts_list:vars]
ansible_ssh_user=root ansible_ssh_pass=default_password

2) Define a simple playbook containing that tasks that you want execute on the managed nodes

    root@node1:~# cat play.yoman
---
- hosts: hosts_list
  tasks:
  - name: "Build hosts file"
    copy: src=/root/yoman.txt dest=/tmp/felixtemp owner=user group=adm mode=0755

3) Execute the playbook and check the result

root@node1:~# ansible-playbook -i hosts_list play.yoman

PLAY [hosts_list] *************************************************************

GATHERING FACTS ***************************************************************
fatal: [172.17.0.104] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
fatal: [172.17.0.103] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
ok: [172.17.0.101]
fatal: [172.17.0.102] => Authentication failure.

TASK: [Build hosts file] ******************************************************
ok: [172.17.0.101]

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/play.yoman.retry

172.17.0.101               : ok=2    changed=0    unreachable=0    failed=0
172.17.0.102               : ok=0    changed=0    unreachable=1    failed=0
172.17.0.103               : ok=0    changed=0    unreachable=1    failed=0
172.17.0.104               : ok=0    changed=0    unreachable=1    failed=0

You obtain the list of server where the task has executes and the list of server where the task failes with the reasons(unreachable server or wrong password). You obtain also the subset of server where the file, its permission and content wasn't changed because they are already updated

1
  • thanks very much for a detailed explanation of how to do this. the problem is these boxes are so locked down in our environment, i have access to VMware, where i could possibly spin up a linux box to do this, but they would require permission for me to do this first. i would like to try this on my own time anyway, looks like a nice resume builder. thank you. Apr 20, 2015 at 19:57
1

None of the previous comments used expect inside a bash script as the OP asked.

Recently, I needed to do a similar task, so here is my solution using expect:

expect <<- DONE
    set timeout 1
    spawn scp yoman.txt root@${ip}:/felixtemp
        while 1 {
            expect {
                "no)?"   {
                    send "yes\n"
                }
                "denied" {
                    log_file fail.txt
                    send_log "Couldn't log in to ${ip}.\n";
                    exit 1
                }
                "assword:" {
                    send "${password}\n"
                }
                "100%" {
                     break;
                }

            }
        }
    expect eof
DONE

Explanation of the expect syntax:

  • while 1 { }: makes an infinite loop. Inside the loop, I define different expect cases.
  • log_file fail.txt: create a file called fail.txt.
  • send_log "text": sends text to log_file.
  • break: breaks out of the infinite loop and ends the expect section.
0

Why don't you use sshpass ?


Loop with a table:

tab=(
     1.2.3.4
     4.3.2.1
     ...
);

for (( i = 1; i < ${#tab[*]}; i++ )) {
        echo ${tab[i]};
        ...
}

You'll need some conditions for specific IP addresses to set the right password.


SCP

sshpass -p $PASSWORD scp -o StrictHostKeyChecking=no $FILE $USER@$HOST:$PATH

According to the manpage, return values are:

0 Success

1 Invalid command line argument

2 Conflicting arguments given

3 General runtime error

4 Unrecognized response from ssh (parse error)

5 Invalid/incorrect password

6 Host public key is unknown. sshpass exits without confirming the new key.

Useful if you want to print $HOST in a file with the return value:

sshpass -p $PASSWORD scp -o StrictHostKeyChecking=no $FILE $USER@$HOST:$PATH

if [ $? -ne 0 ]
then
    echo $HOST:$? >> file.txt;
fi

SSH

To send commands:

sshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no $USER@$HOST $CMD

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .