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?
Advertisement
Answer
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=...
, available starting with bash 4.3) 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