7

I need to have an array for each "section" in the file containing:

[array0]
value1=asdf
value2=jkl

[array1]
value1=1234
value2=5678

I want to be able to retrieve these values like this:

echo ${array0[value1]}
echo ${array0[value2]}

echo ${array1[value1]}
echo ${array1[value2]}

Any thoughts on how to accomplish this? (Explanations would be a bonus)

I've already read these anwsers but none do exactly what I want to do.

Read a config file in BASH without using "source"

BASH Parsing variables from config file

Array like data structure in bash (config file)?

Community
  • 1
  • 1
AJ Ferguson
  • 73
  • 1
  • 1
  • 5
  • Do you have to use Bash? This sort of thing would be completely trivial with e.g. Ruby and YAML or Python and JSON or Lua or a bunch of other things. – John Zwinck Apr 19 '14 at 07:19
  • I think it would be best for it to use Bash for what I am doing. I need to use the screen command and other things related to that. – AJ Ferguson Apr 19 '14 at 07:21
  • Some more detail about what you are actually trying to do would be helpful. – chepner Apr 19 '14 at 14:21

4 Answers4

7

with bash v4, using associative arrays, store the properties from the config file as actual bash variables:

$ while read line; do 
    if [[ $line =~ ^"["(.+)"]"$ ]]; then 
        arrname=${BASH_REMATCH[1]}
        declare -A $arrname
    elif [[ $line =~ ^([_[:alpha:]][_[:alnum:]]*)"="(.*) ]]; then 
        declare ${arrname}[${BASH_REMATCH[1]}]="${BASH_REMATCH[2]}"
    fi
done < config.conf

$ echo ${array0[value1]}
asdf

$ echo ${array1[value2]}
5678

$ for i in "${!array0[@]}"; do echo "$i => ${array0[$i]}"; done
value1 => asdf
value2 => jkl

$ for i in "${!array1[@]}"; do echo "$i => ${array1[$i]}"; done
value1 => 1234
value2 => 5678
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
3

One eval-free, 100% pure Bash possibility:

#!/bin/bash

die() {
   printf >&2 "%s\n" "$@"
   exit 1
}

aryname=''
linenb=0
while read line; do
   ((++linenb))
   if [[ $line =~ ^[[:space:]]*$ ]]; then
      continue
   elif [[ $line =~ ^\[([[:alpha:]][[:alnum:]]*)\]$ ]]; then
      aryname=${BASH_REMATCH[1]}
      declare -A $aryname
   elif [[ $line =~ ^([^=]+)=(.*)$ ]]; then
      [[ -n aryname ]] || die "*** Error line $linenb: no array name defined"
      printf -v ${aryname}["${BASH_REMATCH[1]}"] "%s" "${BASH_REMATCH[2]}"
   else
      die "*** Error line $linenb: $line"
   fi
done

Reads on standard input. If you want to read from a file, change the done by:

done < "filename"

Lines of the form

space and funnŷ sÿmbòl=value that will have an equal sign: look = it's funny

are allowed

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • I like this answer but I find it more convenient to use array0[value1] instead of array0[0]. It better suits my needs. (Updated question) – AJ Ferguson Apr 19 '14 at 16:49
  • @AJFerguson Nice to read this! I actually had to tweak things in a very unnatural way to fit your original requirements! Let me edit the answer to fit your new (and much more natural) requirements. – gniourf_gniourf Apr 19 '14 at 16:57
  • @AJFerguson changed (simplified) to fit your new requirements. – gniourf_gniourf Apr 19 '14 at 17:07
1

You can declare array in bash scripts with

declare -a <array_name>=(value1 value2 value 3)

Then you can use them like this

echo ${<array_name>[index]}

Edit:

Ok, to construct arrays from config file. I would recommend to have a different file for each array you would like to create.

So here are the steps

1.config file (create a file and place your values in it)

100
200
300

2.script file (read values from file and prepare an array)

    array=()

    #setup array
    while IFS=$'\n' read -a config
    do
      array+=(${config})
    done < file_name

    #access values
    echo ${array[0]}
    echo ${array[1]}

IFS denotes the delimiter
-a specifies the array name you want to extract to, so that you can access them inside the while loop.

Deva
  • 433
  • 3
  • 11
0

I am about to go out, but I think you can do something like this (untested) and maybe someone clever , like @anubhava, will pick it up and finish it off...

eval $(gawk -F= '/^\[/{name=gensub(/\[|\]/,"","g");x=0} /=/{print "name[",x++,"]=",$2," "}' config)

Basically, when it sees a line starting with "[" it picks up the array name in the variable name and strips off the square brackets with gensub(). Then, when it sees a line with "=" in it, it outputs the array name and an increasing index "x" for eval to pick up.

Gotta dash - look at the examples for stat -s here.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432