Jes Jes - 3 months ago 7
Bash Question

Bash shell Function that Reads a file to an array

I am writing a shell script function that reads a given file and store the data into a 2D array.

File.txt

a1 b1 c1
a2 b2 c2
a3 b3 c3
# Let's just assume 3 rows


The idea is to pass the storage by
reference
using
eval
, with the function signature
read_data File.txt array num_rows


read_data() {

if [ ! -f $1 ]; then
echo "Failed to read hosts ($1 not found)";
exit;
fi

while read -r line; do

# skip the comments
{ [[ "$line" =~ ^#.*$ ]] || [[ -z $line ]] ;} && continue

# Parse the line
read -a tokens <<< $line

if [ ${#tokens[@]} == 3 ]; then

# Extract the user/host/home
eval $2[\$$3, 0]=\${tokens[0]}
eval $2[\$$3, 1]=\${tokens[1]}
eval $2[\$$3, 2]=\${tokens[2]}
eval $3=$(($3+1))

else
echo "Wrong line format '$line'"
fi

done < $1
}


After calling below

declare -a array
num_rows=0
read_data File.txt array num_rows


What I get is
num_rows
equals to
3
but the contents stored in array is

pnt_data() {
for ((i=0; i<$2; i++)); do

eval a=\${$1[$i, 0]}
eval b=\${$1[$i, 1]}
eval c=\${$1[$i, 2]}

echo $a $b $c
done
}

pnt_data array num_rows

a3 b3 c3
a3 b3 c3
a3 b3 c3


What happen to this? Is there anything wrong with my function?

Answer

Array indices are evaluated in an arithmetic context, and the comma operator works by evaluating the left-hand operand, ignoring it, then evaluating to the right-hand operand. For example:

$ echo $((3,0))
0
$ echo $((3+9,0))
0
$ echo $((a,0))
0

As a result, all of the following assignments are equivalent to foo[0]=bar:

foo[3,0]=bar     # ignore 3
foo[3+9,0]=bar   # 3+9=12, ignore 12
foo[a,0]=bar     # ignore whatever the value of a is

If you want to simulate multidimensional indices, you'll need to use an associative array, so that the string used as an index does not undergo arithmetic expansion before using it.

declare -A array  # capital A, not lowercase a
num_rows=0
read_data File.txt array num_rows