Passing Associative Array as Argument with Bash

Passing associative array as argument with Bash

I don't think you can pass associative arrays as an argument to a function. You can use the following hack to get around the problem though:

#!/bin/bash

declare -A weapons=(
['Straight Sword']=75
['Tainted Dagger']=54
['Imperial Sword']=90
['Edged Shuriken']=25
)

function print_array {
eval "declare -A arg_array="${1#*=}
for i in "${!arg_array[@]}"; do
printf "%s\t%s\n" "$i ==> ${arg_array[$i]}"
done
}

print_array "$(declare -p weapons)"


Output

Imperial Sword ==> 90   
Tainted Dagger ==> 54
Edged Shuriken ==> 25
Straight Sword ==> 75

bash: pass associative array as a parameter to another script

If you are only calling script2.sh from inside script1.sh, then all you need to do (as @markp-fuso pointed out) is source script2.sh and it will run in the current context with all the data already loaded.

If you really want it to be on the command line, then pass it as key=val and have your code in script2.sh check each of it's args for that format and set them in an associative array.

declare -A map=()
for arg in "$@"
do if [[ "$arg" =~ ^[A-Z]=[0-9]$ ]] # more complex k/v will get ugly
then map[${arg/=?}]=${arg/?=} # as will the assignment w/o eval
fi
done
# And finally, just to see what got loaded -
declare -p map

$: script2.sh C=3 A=1
declare -A map=([A]="1" [C]="3" )

As mentioned above, a more complicated possible set of key names and/or values will require a suitably more complex test as well as assignment logic. Clearly, for anything but the simplest cases, this is going to quickly get problematic.

Even better, set up a full getopts loop, and pass your args with proper indicators. This takes more design and more implementation, but that's what it takes to get more functionality.

Shell script: Pass associative array to another shell script

Passing Associative array to sub script

Associative arrays and regular arrays are not well exported. But you could pass variables by using declare -p in some wrapper call.

First script:

#!/bin/bash
declare -A cmdOptions=( [branch]=testing [function1key]=function1value
[directory]=someDir [function2key]=function2value )
declare -a someArray=( foo "bar baz" )

declare -x someVariable="Foo bar baz" # Using *export* for simple variables

bash -c "$(declare -p cmdOptions someArray someVariable);. anothershellscript.sh"

Note syntax . anothershellscript.sh could be replaced by source anothershellscript.sh.

Doing so, prevent need of temporary files or temporary variable and keep STDIN/STDOUT free.

Then your anothershellscript.sh could use your variables as is.

#!/bin/bash

declare -p cmdOptions someArray someVariable

could work.

How can I update an associative array inside a function passing it by parameter?

When you use declare in a function, you're actually making the variable local. See help declare at a bash prompt.

Use a nameref (requires bash version 4.3+):

function update_array
{
local FILE_NAME=$1
local -n MAP=$2 # MAP is now a _reference_ to the caller's variable
# the rest stays the same
for PRICING_FIELD in $(jq -c -r '.fields[]' "${FILE_NAME}")
do
FIELD_KEY=$(jq -r '.label' <<< "${PRICING_FIELD}")
MAP["${FIELD_KEY}"]=${PRICING_FIELD}
done
}

then you simply pass the array name

declare -A PRIVATE_FIELDS
update_array "myFile.json" PRIVATE_FIELDS

declare -p PRIVATE_FIELDS

To more efficiently iterate over the JSON file:

$ jq -c -r '.fields[] | "\(.label)\t\(.)"' file.json
key1 {"label":"key1","value":"value1","other":"other1"}
key2 {"label":"key2","value":"value2","other":"other2"}

That's assuming the labels don't contain any tab characters.


Using that, plus your older bash version, you can do this

Assuming that the result arrays will be in the global scope

update_array() {
local filename=$1 varname=$2
local -A map
while IFS=$'\t' read -r label json; do
map[$label]=$json
done < <(
jq -c -r '.fields[] | "\(.label)\t\(.)"' "$filename"
)
eval declare -gA "$varname=$(declare -p map | cut -d= -f2-)"
}

You'd call it like

$ echo $BASH_VERSION
4.2.45(1)-release

$ update_array tmp/file.json myArray

$ declare -p myArray
declare -A myArray='([key2]="{\"label\":\"key2\",\"value\":\"value2\",\"other\":\"other2\"}" [key1]="{\"label\":\"key1\",\"value\":\"value1\",\"other\":\"other1\"}" )'

$ for label in "${!myArray[@]}"; do
> printf '"%s" => >>%s<<\n' "$label" "${myArray[$label]}"
> done
"key2" => >>{"label":"key2","value":"value2","other":"other2"}<<
"key1" => >>{"label":"key1","value":"value1","other":"other1"}<<

Adding to Bash associative arrays inside functions

EDIT: Found the duplicate. This is basically the same answer as this one.


You can use a reference variable for that, see help declare:

declare [-aAfFgilnrtux] [-p] [name[=value] ...]

[...]

-n make NAME a reference to the variable named by its value

[...]

When used in a function, declare makes NAMEs local, as with the local command.

f() {
declare -n paramblock="$1"
# example for reading (print all keys and entries)
paste <(printf %s\\n "${!paramblock[@]}") <(printf %s\\n "${paramblock[@]}")
# example for writing
paramblock["key 1"]="changed"
paramblock["new key"]="new output"
}

Example usage:

$ declare -A a=(["key 1"]="input 1" ["key 2"]="input 2")
$ f a
key 2 input 2
key 1 input 1
$ declare -p a
declare -A a=(["key 2"]="input 2" ["key 1"]="changed" ["new key"]="new output" )

This works very well. The only difference to an actual associative array I found so far is, that you cannot print the referenced array using declare -p as that will only show the reference.

Passing arrays as parameters in bash

You can pass multiple arrays as arguments using something like this:

takes_ary_as_arg()
{
declare -a argAry1=("${!1}")
echo "${argAry1[@]}"

declare -a argAry2=("${!2}")
echo "${argAry2[@]}"
}
try_with_local_arys()
{
# array variables could have local scope
local descTable=(
"sli4-iread"
"sli4-iwrite"
"sli3-iread"
"sli3-iwrite"
)
local optsTable=(
"--msix --iread"
"--msix --iwrite"
"--msi --iread"
"--msi --iwrite"
)
takes_ary_as_arg descTable[@] optsTable[@]
}
try_with_local_arys

will echo:

sli4-iread sli4-iwrite sli3-iread sli3-iwrite  
--msix --iread --msix --iwrite --msi --iread --msi --iwrite

Edit/notes: (from comments below)

  • descTable and optsTable are passed as names and are expanded in the function. Thus no $ is needed when given as parameters.
  • Note that this still works even with descTable etc being defined with local, because locals are visible to the functions they call.
  • The ! in ${!1} expands the arg 1 variable.
  • declare -a just makes the indexed array explicit, it is not strictly necessary.


Related Topics



Leave a reply



Submit