Home > OS >  BASH - Create arrays from lines of csv file, where first entry is array name
BASH - Create arrays from lines of csv file, where first entry is array name

Time:12-07

I'm learning to script in Bash. I have an CSV file, which contains next lines:

numbers,one,two,three,four,five
colors,red,blue,green,yellow,white
custom-1,a,b,c,d,e
custom 2,t,y,w,x,z

Need to create arrays from this, where first entry is array name, eg.

number=(one,two,three,four,five)
colors=(red,blue,green,yellow,white)
custom-1=(a,b,c,d,e)
custom 2=(t,y,w,x,z)

Here is my script:

IFS=","
while read NAME VALUES ; do
    declare -a $NAME
    arrays =($NAME)
    IFS=',' read -r -a $NAME <<< "${VALUES[0]}"
done < file.csv

When I try with csv file, containing only two first string (numbers and colors), code works well. And if i try to with number, colors, custom-1, custom-2, there is error during reading csv:

./script.sh: line 5: declare: `custom-1': not a valid identifier
./script.sh: line 7: read: `custom 2': not a valid identifier

because bash does not allow special characters in variable names, as far as I understand. Is there any way to avoid this?

CodePudding user response:

A partial solution. You'll have to split the values for accessing them:

unset __ARRAYS__
declare -A __ARRAYS__

while IFS=',' read -r name values
do
    __ARRAYS__[$name]=$values
done <<'EOF'
numbers,one,two,three,four,five
colors,red,blue,green,yellow,white
custom-1,a,b,c,d,e
custom 2,t,y,w,x,z
EOF

# debug info
declare -p __ARRAYS__
echo
for name in "${!__ARRAYS__[@]}"
do
    printf '%q:\n' "$name"
    IFS=',' read -r -a values <<< "${__ARRAYS__[$name]}"
    printf '\t%s\n' "${values[@]}"
done

output:

declare -A __ARRAYS__='([custom 2]="t,y,w,x,z" [custom-1]="a,b,c,d,e" [numbers]="one,two,three,four,five" [colors]="red,blue,green,yellow,white" )'

custom 2:
    t
    y
    w
    x
    z
custom-1:
    a
    b
    c
    d
    e
numbers:
    one
    two
    three
    four
    five
colors:
    red
    blue
    green
    yellow
    white

CodePudding user response:

As you cannot use the first column of your CSV file as bash array names one option would be to generate valid names using a counter (e.g. arrayN). If you want to access your data using the values of this first column you would also need to store them somewhere with the corresponding counter value. An associative array (declare -A names=()) would be perfect. Last but not least, the namerefs (declare -n arr=...) will be convenient to store and access your data. Example:

declare -i cnt=1
declare -A names=()
while IFS=',' read -r -a line; do
  names["${line[0]}"]="$cnt"
  declare -n arr="array$cnt"
  unset line[0]
  declare -a arr=( "${line[@]}" )
  ((cnt  ))
done < foo.csv

Now, to access the values corresponding to, let's say, entry custom 2, first get the corresponding counter value, declare a nameref pointing to the corresponding array and voilà:

$ cnt="${names[custom 2]}"
$ declare -n arr="array$cnt"
$ echo "${arr[@]}"
t y w x z

Let's declare a function for easier access:

getdata () {
  local -i cnt="${names[$1]}"
  local -n arr="array$cnt"
  [ -z "$2" ] && echo "${arr[@]}" || echo "${arr[$2]}"
}

And then:

$ getdata "custom 2"
t y w x z
$ getdata "colors"
red blue green yellow white
$ getdata "colors" 3
yellow
  • Related