With a helper function:
#!/bin/bash
to_param_list () {
declare -n outlist=$1
declare -n inhash=$2
for param in "${!inhash[@]}"; do
outlist+=( "--$param=${inhash[$param]}" )
done
}
declare -A my_vars=( ["key1"]="value1" ["key2"]="value" )
to_param_list list my_vars
my_script.sh "${list[@]}"
The final command in the above script would expand to the equivalent of having written
my_script.sh "--key2=value" "--key1=value1"
The to_param_list
function takes the name of an array variable and the name of an associative array variable and uses these to create two "name reference" variables in the function (namerefs were introduced in bash
release 4.3). These are then use to populate the given array variable with the keys and values in the appropriate format from the associative array.
The loop in the function iterates over "${!inhash[@]}"
, which is the list of individually quoted keys in your associative array.
Once the function call returns, the script would use the array to call your other script or command.
Running the above with
declare -A my_vars=( ["key1"]="hello world" ["key2"]="some thing" ["key3"]="* * *" )
to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"
the script would output
Arg: --key2=some thing
Arg: --key3=* * *
Arg: --key1=hello world
This shows that the options are generated without word splitting or filename globbing coming into effect. It also shows that the order of the keys may not be preserved since accessing the keys out of an associative array will do so in a fairly random order.
You can't really use a command substitution safely here as the result of it would be a single string. If unquoted, this string would then be split on whitespace characters (by default), which would additionally split both the keys and values of your associative array. The shell would also perform filename globbing on the resulting words. Double quoting the command substitution would not help as that would result in calling your my_script.sh
with a single argument.
Regarding your issue with makeself
:
The makeself
script does this with the arguments to your installer script:
SCRIPTARGS="$*"
This saves the arguments as a string in $SCRIPTARGS
(concatenated, separated by spaces). This later gets inserted into the self-extracting archive as-is. For the options to be parsed correctly when they are re-evaluated (which they are when running the installer), you will have to provide an extra set of quotes in the values of the parameters for them to be properly delimited.
installer_param_array=( ["upgrade-from"]="'19 .2.0'" ["upgrade-to"]="'19.3.0'" )
Note that this is not a bug in my code. It's just a side effect of makeself
producing shell code based on user-supplied values.
Ideally, the makeself
script should have written each of the provided arguments with an extra set of quotes around them, but it doesn't, presumable because it's difficult to know what effect that may have. Instead, it leaves it to the user to provide these extra quotes.
Rerunning my test from above, but now with
declare -A my_vars=( ["key1"]="'hello world'" ["key2"]="'some value'" ["key3"]="'* * *'" )
to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"
produces
Arg: --key2='some value'
Arg: --key3='* * *'
Arg: --key1='hello world'
You can see that these strings, when re-evaluated by the shell, would not get split on spaces.
Obviously, you could use your initial associative array and instead add the quotes in the to_param_list
function by changing
outlist+=( "--$param=${inhash[$param]}" )
into
outlist+=( "--$param='${inhash[$param]}'" )
Either of these changes to your code would include the single quotes in the values of the options, so a re-evaluation of the values would become necessary.
my_script.sh "$(declare -p thearray)$"
. Inmyscript.sh
you read it withsource /dev/stdin <<<"$1"
Then you havethearray
in your script. You can have other arguments alongside the array. You can pass many variables:my_script.sh "$(declare -p var1 var2 ...)"
in this single argument.