Bash – Work-Around to storing array values in an environment variable then calling from Bash script

bashshell-script

I looked around for an answer to the question "Can I use an array as an environment variable and then call it from a Bash shell script" and came to the conclusion that it was not "officially" supported as (outlined here, and here).

However I really had a need to do this and came up with a "work-around" and wanted to get your opinions.

Setup: I created a variable in my .profile file that looks something like:

HOST_NAMES='server1 server2 server3'

Then at the begining of my shell script I wrote:

SRVR_ARRAY=($(echo $HOST_NAMES | awk '{for (i = 1; i <=NF; i++) print $i " "}'))

Later on in the script when it was time to do some work, I called:

for h in "${SRVR_ARRAY[@]}"; do
ping $h
done

And it worked! Now I'm sure this is Bash shell scripting jerry-riggin at its finest but wanted to see if anyone could see any risks in this usage?

Best Answer

The problems you might face are the usual ones: splitting and globbing.

You can't have strings with whitespace, since they'll be split to separate array items, and anything resembling a glob character (*?[]) will be expanded to matching filenames. Probably not a problem with host names though, but in general, it is.

This is a common issue, at repeatedly discussed, see e.g. Expansion of a shell variable and effect of glob and split on it and Security implications of forgetting to quote a variable in bash/POSIX shells


To work around those, you'd need to a) set IFS to something other than whitespace and separate your strings with that, and b) disable globbing with set -f.

This would use | as a separator and split the string. You could use any character not needed in the values, maybe even some control character like \001 (Use $'\001' in Bash to create it).

#!/bin/bash
string='foo|bar'
IFS='|'; set -f
array=($string)        # split it

IFS='|'
string="${array[*]}"   # merge it back together

Also, like they say in the comments, if your variable only has hostnames and you are fine with splitting on whitespace, you don't need the awk to split it. The shell will do it when assigning to array or starting the loop:

SRVR_ARRAY=( $HOST_NAMES )
for x in $HOST_NAMES ; do ...

(Again, you'll get trouble if HOST_NAMES contains glob characters.)

Actually, that's what is going to happen even with your example: awk prints something, the shell catches it, splits it on words, since the $() was not inside double-quotes, and then saves the words separately in the array.

Related Question