Lorccan
Lorccan

Reputation: 823

Bash Associative Array from String?

A command emits the string: "[abc]=kjlkjkl [def]=yutuiu [ghi]=jljlkj"

I want to load a bash associative array using these key|value pairs, but the result I'm getting is a single row array where the key is formed of the first pair [abc]=kjlkjkl and the value is the whole of the rest of the string, so: declare -p arr returns declare -A arr["[abc]=kjlkjkl"]="[def]=yutuiu [ghi]=jljlkj"

This is what I am doing at the moment. Where am I going wrong please?

declare -A arr=()
while read -r a b; do
    arr["$a"]="$b"
done < <(command that outputs the string "[abc]=kjlkjkl [def]=yutuiu [ghi]=jljlkj")

Upvotes: 1

Views: 2217

Answers (4)

wjandrea
wjandrea

Reputation: 33159

You need to parse it: split the string on spaces, split each key-value pair on the equals sign, and get rid of the brackets.

Here's one way, using tr to replace the spaces with newlines, then tr again to remove all brackets (including any that occur in a value), then IFS="=" to split the key-value pairs. I'm sure this could be done more effectively, like with AWK or Perl, but I don't know how.

declare -A arr=()
while IFS="=" read -r a b; do
    arr["$a"]="$b"
done < <(
    echo "[abc]=kjlkjkl [def]=yutuiu [ghi]=jljlkj" |
        tr ' ' '\n' |
        tr -d '[]'
    )

echo "${arr[def]}"  # -> yutuiu

See Cyrus's answer for another take on this, with the space and equals steps combined.

Upvotes: 3

Cyrus
Cyrus

Reputation: 88939

Append this to your command which outputs the string:

| tr ' =' '\n ' | tr -d '[]'

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141891

The "proper" good™ solution would be to write your own parser and tokenize the input. For example read the input char by char, handle [ and ] and = and space and optionally quoting. After parsing the string, assign the output to an associative array.

A simple way could be:

echo "[abc]=kjlkjkl [def]=yutuiu [ghi]=jljlkj" |
xargs -n1 |
{
declare -A arr;
while IFS= read -r line; do
   if [[ "$line" =~ ^\[([a-z]*)\]=([a-z]*)$ ]]; then
       arr[${BASH_REMATCH[1]}]=${BASH_REMATCH[2]}
   fi
done
declare -p arr
}

outputs:

declare -A arr=([abc]="kjlkjkl" [ghi]="jljlkj" [def]="yutuiu" )

Upvotes: 0

choroba
choroba

Reputation: 242333

You can use the "eval declare" trick - but be sure your input is clean.

#! /bin/bash
s='[abc]=kjlkjkl [def]=yutuiu [ghi]=jljlkj'
eval declare -A arr=("$s")
echo ${arr[def]}  # yutuiu

If the input is insecure, don't use it. Imagine (don't try) what would happen if

s='); rm -rf / #'

Upvotes: 0

Related Questions