-1

I found an implementation of the associative array here and would like to understand what the code actually does. Here are the piece of the code I don't understand, and would appreciate the explanation. '

put() {
    if [ "$#" != 3 ]; then exit 1; fi  
    mapName=$1; key=$2; value=`echo $3 | sed -e "s/ /:SP:/g"`  #dont understand 
    eval map="\"\$$mapName\""  **#dont understand** 
    map="`echo "$map" | sed -e "s/--$key=[^ ]*//g"` --$key=$value"  #dont understand 
    eval $mapName="\"$map\""  #dont understand 
}
get() {
    mapName=$1; key=$2

    map=${!mapName}
    #dont understand 
    value="$(echo $map |sed -e "s/.*--${key}=\([^ ]*\).*/\1/" -e 's/:SP:/ /g' )"  
}

getKeySet() {
    if [ "$#" != 1 ]; 
    then 
        exit 1; 
    fi

    mapName=$1; 

    eval map="\"\$$mapName\""

    keySet=`
           echo $map | 
           sed -e "s/=[^ ]*//g" -e "s/\([ ]*\)--/\1/g"   #dont understand 
          ` 
}

Thanks.

Community
  • 1
  • 1
vehomzzz
  • 42,832
  • 72
  • 186
  • 216

3 Answers3

2

So first in order of don't understands:

  1. this simply checks you always have 3 arguments to the function and if different number is provided exits with 1(error)
  2. This escapes the space chars by replacing them with :SP: so Hi how are you becomes Hi:SP:how:SP:are:SP:you
  3. The map is stored in a var with name the first argument provided to put. So this line adds to the var the following text --$key=$value and here key is the second argument and value is the escaped third argument
  4. get simply stores in value the value for the key provided as second argument(first one is the name of the map)

To understand better try printing the variable you decided to use for your map after each operation. It's easy you'll see ;) Hope this makes it clear.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
1

I'll try to explain every line you highlighted:

    if [ "$#" != 3 ]; then exit 1; fi   #dont understand

If there aren't exactly three arguments, exit with error.

    mapName=$1; key=$2; value=`echo $3 | sed -e "s/ /:SP:/g"`  #dont understand 
  1. Set the variable $mapName with the value of first argument
  2. Set the variable $key with the value of second argument
  3. Set the variable $value with the value of third argument, after replacing spaces with the string :SP:.

    map="`echo "$map" | sed -e "s/--$key=[^ ]*//g"` --$key=$value"  #dont understand
    

This will edit the $map variable, by removing the first occurrance of the value of $key followed by = and then by non-space characters, and then append the string -- followed by the value of $key, then by = and finally the value of $value.

    eval $mapName="\"$map\""  #dont understand 

This will evaluate a string that was generated by that line. Suppose $mapName is myMap and $map is value, the string that bash will evaluate is:

myMap="value"

So it will actually set a variable, for which its name will be passed by a parameter.

    map=${!mapName}

This will set the variable $map with the value the variable that has the same name as the value in $mapName. Example: suppose $mapName has a, then $map would end up with the contents of a.

    value="$(echo $map |sed -e "s/.*--${key}=\([^ ]*\).*/\1/" -e 's/:SP:/ /g' )"  

Here we set the value of the $value variable as the value of the $map variable, after a couple of edits:

  1. Extract only the contents that are specified in the expression between parenthesis in the sed expression, which matches characters that aren't spaces. The text before it specifies where the match should start, so in this case the spaces must start after the string -- followed by the value of the $key variable, followed by =. The `.*' at the start and the end matches the rest of the line, and is used in order to remove them afterwards.
  2. Restore spaces, ie. replace :SP: with actual spaces.

    eval map="\"\$$mapName\""
    

This creates a string with the value "$mapName", ie. a dollar, followed by the string contained in mapName, surrounded by double quotes. When evaluated, this gets the value of the variable whose name is the contents of $mapName.

Hope this helps a little =)

0

Bash, since version 4 and upwards, has builtin associative arrays.
To use them, you need to declare one with declare -A

#!/usr/bin/env bash

declare -A arr    # 'arr' will be an associative array
                  # now fill it with: arr['key']='value'
arr=(
    [foo]="bar"
    ["nyan"]="cat"
    ["a b"]="c d"
    ["cookie"]="yes please"
    ["$USER"]="$(id "$LOGNAME")"
)

#    -- cmd --                  -- output --
echo "${arr[foo]}"            # bar
echo "${arr["a b"]}"          # c d

user="$USER"
echo "${arr["$user"]}"        # uid=1000(c00kiemon5ter) gid=100...

echo "${arr[cookie]}"         # yes please
echo "${arr[cookies]}"        # <no output - empty value> :(

# keys and values
printf '%s\n' "${arr[@]}"     # print all elements, each on a new line
printf '%s\n' "${!arr[@]}"    # print all keys,     each on a new line

# loop over keys - print <key :: value>
for key in "${!arr[@]}"
do printf '%s :: %s\n' "$key" "${arr["$key"]}"
done

# you can combine those with bash parameter expansions, like
printf '%s\n' "${arr[@]:0:2}" # print only first two elements
echo "${arr[cookie]^^}"       # YES PLEASE
c00kiemon5ter
  • 16,994
  • 7
  • 46
  • 48