0

I have an array, $HOSTS, which contains a set of hostnames. I have a database table that has a set of IP addresses. I want to go through each IP address in that table and send a command (hostname -s) to that address, and add the output to $HOSTS. The remote servers have no PKI configured and the script must run automatically without human interaction. It's a closed network and the root password is stored as variables in the script ($SERVERPASSWORD). And before anyone says it I realize it's not a good idea to have the root password in a variable like this, but in this case it's more important for there to not be any human interaction than for the root password to be protected.

Here's the relevant part of my current (sanitized) script:

 for IP in $(sql query that returns a list of IP addresses);do
    HOSTS=($(expect -c 'spawn ssh root@$IP "hostname -s"; expect "assword:";send ""$SERVERPASSWORD"\r";interact' ${HOSTS[@]}))
done

The result of this is a failure, saying:

Can't read "IP": no such variable while executing "spawn ssh root@$IP "hostname -s""

I can put an echo $IP in place of that and it works as expected, so I know the IP variable is being filled in the loop.

After some Googling, it seems from what I understand that expect needs to have its own variable set and can't use Bash variables, but all the solutions I found were for actual Expect scripts, and not for using expect within a Bash script. I tried modifying my command to be expect set IP $IP -c 'spawn ....' but that didn't seem to take.

What can I do to make expect read the value of $IP in this For loop?

2
  • $IP is single-quoted, so the shell does not expand it. So is $SERVERPASSWORD, I think. Can you take it from here? I don't want to touch the code because I don't get the point of HOSTS= or interact ("it's more important for there to not be any human interaction" + interact = ???). Can't you use sshpass? Commented Apr 28, 2020 at 16:29
  • sshpass isn't an option because the server doesn't have it and in this particular case, no tools can be used that aren't already in place. As for interact, I actually just pulled the basic format of that expect command from a script I found with google and it was doing what I wanted, so I left it.However I'm thinking I'm going to go a different route than expect now.
    – Kefka
    Commented Apr 28, 2020 at 17:03

1 Answer 1

0

Get out of the habit of using ALLCAPS variable names, leave those as reserved by the shell. One day you'll write PATH=something and then wonder why your script is broken.

Also you can use array+=( "new element" ) to append to an array.

Below, I've expanded the one-liner to code for readability.


This is just a quoting problem in the shell. There are a few ways to address it:

  1. use double quotes for the expect body. That means you'll have to handle expect quoting differently: you could backslash all the double quotes (gross) or use braces which are Tcl's single quoting mechanism:

    hosts+=( "$(
        expect -c "
            spawn ssh root@$ip {hostname -s}
            expect {assword:}
            send {$serverPassword}; send \\r
            expect eof
        "
    )" )
    
  2. pass the variables through the environment. Here I'm using a quoted heredoc so you don't need to fuss about quoting:

    hosts+=( "$(
       ip="$ip" pw="$serverPassword" expect <<<'END_EXPECT'
            spawn ssh root@$env(ip) "hostname -s"
            expect "assword:"
            send "$env(serverPassword)\r"
            expect eof
    END_EXPECT
    )" )
    

You must log in to answer this question.

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