1

I am trying to do the following indirect task:

host_1=(192.168.0.100 user1 pass1)
host_2=(192.168.0.101 user2 pass2)

hostlist=( "host_1" "host_2" )

for item in ${hostlist[@]}; do

current_host_ip=${!item[0]}
current_host_user=${!item[1]}
current_host_pass=${!item[2]}

echo "IP: $current_host_ip User: $current_host_user Pass: $current_host_pass"

done

I'm trying to understand how should I perform this indirect request so I pull the hostname from the array "hostlist", and then I should do indirect request to pull the host 1 IP, user and pass. But when I'm trying to do it, I'm stuck with either only first variable (only IP), or all variables inside one (if I add [@] into the end of variable name), empty result, or numbers from array. I can't understand how can I first copy the host_1 array into current_ variables and then (after my script does some work) I need to pass the host_2 variables to the same variables current_.

Can you pinpoint my mistake? I think this is the solution to the problem I just can't adopt it:

Indirect return of all elements in an array

1
  • The shell doesn't really bend well to 2-D structures like that. If you have the host names and info in e.g. some external data file, it might be easier to just read that directly in a loop, instead of even trying to fit them all into shell arrays to begin with. – ilkkachu Jul 17 '20 at 23:49
5

You could use a name reference to your array variable:

for item in "${hostlist[@]}"; do

  declare -n hostvar=$item
  current_host_ip=${hostvar[0]}
  current_host_user=${hostvar[1]}
  current_host_pass=${hostvar[2]}

  echo "IP: $current_host_ip User: $current_host_user Pass: $current_host_pass"
done

Here, variable hostvar refers to the variable named $item which is either array host_1 or host_2.

Using variable indirection and a copy of the array values:

for item in "${hostlist[@]}"; do

    x=${item}[@]
    y=( "${!x}" )

    current_host_ip=${y[0]}
    current_host_user=${y[1]}
    current_host_pass=${y[2]}

    echo "IP: $current_host_ip User: $current_host_user Pass: $current_host_pass"
done
1
  • Might also be a good idea to name the variables with some fixed prefix so there's less risk of them clashing with some others. So host_foo=(...) and then declare -n hostvar="host_$item" or something like that. – ilkkachu Jul 17 '20 at 23:46
2

Usually when somethings hard to do in shell it's because you're trying to do something you shouldn't be trying to do in shell. Are you sure you shouldn't just be doing:

$ cat tst.sh
#!/usr/bin/env bash

hosts[1]='192.168.0.100 user1 pass1'
hosts[2]='192.168.0.101 user2 pass2'

for item in "${hosts[@]}"; do
    read -r -a current_host <<< "$item"
    echo "IP: ${current_host[0]} User: ${current_host[1]} Pass: ${current_host[2]}"
done

$ ./tst.sh
IP: 192.168.0.100 User: user1 Pass: pass1
IP: 192.168.0.101 User: user2 Pass: pass2

or similar?

1
  • 1
    This is also a good approach, thank you. I will try to choose between name reference and your solution :) –  sxiii Jul 20 '20 at 4:08
-2

As it often happens, I finally find a way to make what I wanted. But I still think that this solution is super-ugly and uses eval instead of indirect request, so I will leave the question open if somebody can make it more pretty, compact and short. Anyway, here's the way I did it:

for item in ${hostlist[@]}; do
    eval ip=\( \${${item}[0]} \)
    eval user=\( \${${item}[1]} \)
    eval pass=\( \${${item}[2]} \)
    echo "IP: $ip User: $user Pass: $pass"
done

That works for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.