How to pass multiple servers to an expect script

expectscripting

I'm trying to use an expect script to change my password on multiple servers, but I'm a little confused as to how to pass the list of servers through to it.

The script that I'm using is as follows:

#!/usr/bin/expect -f
# wrapper to make passwd(1) be non-interactive
# username is passed as 1st arg, passwd as 2nd

set username [lindex $argv 0]
set password [lindex $argv 1]
set serverid [lindex $argv 2]
set newpassword [lindex $argv 3]

spawn ssh -t $serverid passwd
expect "assword:"
sleep 3
send "$password\r"
expect "UNIX password:"
sleep 3
send "$password\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect eof

And I'm trying to run it as such:

[blah@blah ~]$ ./setkey1 blah password 'cat serverlist' meh

which gives me the following output:

spawn ssh -t cat serverid passwd
ssh: cat serverid: Name or service not known
send: spawn id exp6 not open
    while executing
"send "$password\r""
    (file "./setkey1" line 13)

So I then tried running the following for loop:

[blah@blah ~]$ for i in serverid; do `cat serverid`; ./setkey1 blah password $i meh; done

Which gave me the following:

-bash: staging01v: command not found
spawn ssh -t serverid passwd
ssh: serverid: Name or service not known
send: spawn id exp6 not open
    while executing
"send "$password\r""
    (file "./setkey1" line 13)

If I try using the expect script, and just enter in one server name, it works as…um…expected.

What am I doing wrong?

Best Answer

There are many ways to solve this problem. I'd change the order of your arguments to be able to pass in multiple servers.

In the expect program:

foreach {username password newpassword} $argv break
set servers [lrange $argv 3 end]

foreach serverid $servers {
    # your existing code goes here
}

Then from the shell, invoke it like this

./setkey1 userid pass newpass $(cat servers.txt)

If you use bash, you can do

./setkey1 userid pass newpass $(<servers.txt)