Yes, iterate over the indices of the arrays, rather than their values. BTW, The two arrays share indices, so you only need one iterator variable ($i
below).
bash
arrays start from zero, and you can get the number of elements with "${#arrayname[@]}"
- so you need to iterate from zero to one less than that. An array with three elements will have indices 0, 1, and 2 - assuming there were no gaps in the indexing when the array was created. By "gaps", I mean where numeric indices are "missing" (either never inserted into the array or deleted some time after insertion) - e.g. an array might have indices 1, 3, and 5. 2 and 4 are missing.
e.g.
i=0
while [ "$i" -lt "${#NODE_SRVS[@]}" ] ; do
setting $h and $u isn't strictly necessary but makes the code
easier to read, at little performance cost.
h="${NODE_SRVS[$i]}"
u="${NODE_USERS[$i]}"
rsync -rt --delete --info=progress2,stats2
-e ssh -tt
"$EXTENSIONS/"
"$h:/home/$u/.local/share/gnome-shell/extensions/
let i+=1
done
You can also use "${!arrayname[@]}"
to get a list of all the indices - listing only the indices that exist, ignoring any gaps. You can iterate over that with for
:
for i in "${!NODE_SRVS[@]}" ; do
h=....
u=...
rsync ...
done
This is useful for indexed arrays (because of the possibility of gaps), but is especially useful for associative arrays, which brings me to the next alternative: use an associative array (aka "hash"), e.g. where the user names are the keys (indices) and the hostnames/IP addresses are the values (in simple cases like this, it would work just as well to have the IP addresses be the keys and the usernames be the values).
declare -A NODES
NODES=([mf]=192.168.122.60)
NODES+=([lf]=192.168.122.92)
NODES+=([qf]=192.168.122.93)
EXTENSIONS="something"
for u in "${!NODES[@]}"; do
rsync -rt --delete --info=progress2,stats2
-e ssh -tt
"$EXTENSIONS/"
"$u@${NODES[$u]}:/home/$u/.local/share/gnome-shell/extensions/"
done
Sample output with an echo
at the start of the rsync
line:
rsync -rt --delete --info=progress2,stats2 -e ssh -tt something/ qf@192.168.122.93:/home/qf/.local/share/gnome-shell/extensions/
rsync -rt --delete --info=progress2,stats2 -e ssh -tt something/ lf@192.168.122.92:/home/lf/.local/share/gnome-shell/extensions/
rsync -rt --delete --info=progress2,stats2 -e ssh -tt something/ mf@192.168.122.60:/home/mf/.local/share/gnome-shell/extensions/
Note, though, that associative arrays in bash are inherently unordered - that means you'll iterate over the keys in effectively random order. Sometimes this matters, sometimes it doesn't. When it doesn't matter, it's convenient to use an associative array. When it does matter, use two indexed arrays as in the first example above.
When you want the convenience of a hash but need to process the array in the same order it was created, use a hash but also use an indexed array with the keys to the hash as the values of the array. Iterate over the indexed array to get the key names for the hash in the order they were inserted. e.g.
...
ORDER=(mf lf qf)
for u in "${ORDER[@]}"; do
rsync -rt --delete --info=progress2,stats2
-e ssh -tt
"$EXTENSIONS/"
"$u@${NODES[$u]}:/home/$u/.local/share/gnome-shell/extensions/"
done
${!IPS[*]}
instead of quoted"${!IPS[@]}"
- these are all bad habits to get into. In short, never trust that a variable will always contain only the kinds of values you think they contain - even if you're 100% certain you control where those variables get their data from, it only takes one bug, one bad value to break your script or execute code that you didn't intend to be executed. – cas Dec 04 '21 at 04:10eval
keyword is often warned against as a potential source of serious bugs. See Why does my shell script choke on whitespace or other special characters? – cas Dec 04 '21 at 04:12