2

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")
3
  • Are the quotation marks part of the string? Commented Nov 21, 2020 at 19:45
  • are there really [] chars in the output you are passing in? I don't think that is helping. Also, while read IFS="=" -r a b ... might help. Commented Nov 21, 2020 at 19:57
  • Thank you both. The quotation marks are showing the string boundaries. There are none in the string itself. And, yes, there are [] in the string, but I am going to see if I can get output a different way to avoid them. Commented Nov 21, 2020 at 20:16

4 Answers 4

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. As I've noted in @KamilCuk's answer, I can get the data pairs without [] and separated by spaces so I can trim this down to do what I need.
1

Append this to your command which outputs the string:

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

Comments

0

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 / #'

Comments

0

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" )

1 Comment

Thank you. I'm going with @wjandrea's answer as I've found a way to get my output with just pairs with = and separated by spaces.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.