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