2

How do I get an associative array from a query string in Bash? - Attempt:

#!/usr/bin/env bash

# Querystring (implementation stolen from http://stackoverflow.com/q/3919755)

function populate_querystring_array ()
{
    param="$1"
    query_dict="$2"
    #for i in "${array[@]}"; do IFS="=" ; set -- $i; query_dict[$1]=$2; done

    for ((i=0; i<${#param[@]}; i+=2))
    do
        query_dict[${param[i]}]=${param[i+1]}
    done
}

q0='email=foo@bar.com&password=dfsa54'
declare -A querydict
populate_querystring_array "$q0" "$querydict"
printf "$querydict[email]"
A T
  • 13,008
  • 21
  • 97
  • 158

3 Answers3

3
#!/usr/bin/env bash
q0='email=foo@bar.com&password=dfsa54'
declare -A querydict
while IFS== read key value
do
    querydict["$key"]="$value"
done < <(echo "$q0" | sed 's/&/\n/g' )
printf "${querydict[email]}\n"

In the above, 's/&/\n/g' is a sed command that replaces every occurrence of & with a new line. We apply this to q0 so that every parameter assignment is on a separate line. The parameter assignments are then read into the while loop. To read each assignment, IFS== read key value is used. IFS== tells read to treat the equal sign as a word separator. Thus, each assignment is broken into two words: the first is the key and the second is the value. These are then assigned to the associative array querydict with the statement querydict["$key"]="$value".

Putting it in a function

bash differs from most modern programming languages in that its facilities for passing complex data into and out of functions are extremely limited. In the method shown below, the associative array, querydict, is a global variable:

#!/usr/bin/env bash
declare -A querydict
populate_querystring_array () {
    query="$1"
    while IFS== read arg value
    do 
        querydict["$arg"]="$value"
    done < <(echo "$query" | sed 's/&/\n/g' )
}

q0='email=foo@bar.com&password=dfsa54'
populate_querystring_array "$q0"
printf "${querydict[email]}\n"
John1024
  • 109,961
  • 14
  • 137
  • 171
  • Thanks, but I couldn't get that to work in a function. [See ideone](http://ideone.com/uQ6Pwm). – A T Feb 26 '14 at 03:37
  • @AT `bash` is much more primitive than you think. Facilities that you may expect from other programming languages are just not part of `bash`. I did add a `bash` solution with a function to the answer but note that it uses a shared global variable. – John1024 Feb 26 '14 at 04:54
  • Would rather a passed argument. If that's not possible, I can empty querydict using `unset $querydict`; right? - EDIT: looks like there's [a hack](http://stackoverflow.com/a/8879444/587021) to make it work – A T Feb 26 '14 at 05:16
  • @AT If you use `unset`, you will lose the `declare -A` definition. If you just want to empty it, use `querydict=()`. That hack that you link to uses `eval` which is something people work hard to avoid. Why is it that you so strongly want to put that simple four line loop in a function? – John1024 Feb 26 '14 at 07:44
  • Because I need that functionality in a large number of places. – A T Feb 27 '14 at 03:53
1

Below should work:

#!/bin/bash

function qrystring() {
    qry=$1

    while read key value; do
        arr+=(["$key"]="$value")
    done < <(awk -F'&' '{for(i=1;i<=NF;i++) {print $i}}' <<< $qry | awk -F'=' '{print $1" "$2}')
}

q='email=foo@bar.com&password=dfsa54'
declare -A arr

qrystring "$q"

for k in ${!arr[@]}; do
    echo "$k -> ${arr[$k]}"
done

Explanation:

Im using a combination of awk commands to split the string into individual records first, then split on the = sign for kv pair.

I'm using process substitution here otherwise i would be populating a copy of the array.

EDIT:

Using a global variable to house array.

Timmah
  • 2,001
  • 19
  • 18
  • 1
    Tim and @AT, just a minor point: you may want to remove the statement `arr=$2`. This is because,when bash executes the statement `qrystring "$q" "$arr"`, the second argument evaluates to an empty string (because `arr` is empty at that point). Consequently, the assignment statement `arr=$2` sets `arg[0]` (`0` is the default index when none is specified) to an empty string. The effect of this is visible in the final `for` loop which outputs, as one of its lines, the string `0 ->`. – John1024 Feb 27 '14 at 06:01
0

Taking @John1024's answer I use it in a function which returns the associative array by value, simply printing out the contents using array syntax:

function parseQuery {
    local querystring="$*"
    echo -n "("
    echo "${querystring}" | sed 's/&/\n/g' | while IFS== read arg value
    do
        echo -n "[${arg}]='${value}' "
    done
    echo ")"
}

declare -A querydict=$(parseQuery "${QUERY_STRING}" )
Ed Randall
  • 6,887
  • 2
  • 50
  • 45