Reputation: 83
I am trying to read up 3 similar files with different names to different arrays. Because i didn't want to use unnecessary code i am trying to create functions that would accept array names as params, but i am getting error 'command not found'.
hello.sh file code:
#!/bin/bash
declare -a row_1
declare -a row_2
declare -a row_3
load_array()
{
ROW="$2"
let i=0
while read line; do
for word in $line; do
$ROW[$i]=$word
((++i))
done
done < $1
}
load_array $1 row_1
load_array $2 row_2
load_array $3 row_3
Calling this file from terminal with: sh hello.sh 1.txt 2.txt 3.txt
List of errors i am getting:
hello.sh: line 13: row_1[0]=9: command not found
hello.sh: line 13: row_1[1]=15: command not found
hello.sh: line 13: row_1[2]=13: command not found
hello.sh: line 13: row_2[0]=12: command not found
hello.sh: line 13: row_2[1]=67: command not found
hello.sh: line 13: row_2[2]=63: command not found
hello.sh: line 13: row_3[0]=75: command not found
hello.sh: line 13: row_3[1]=54: command not found
hello.sh: line 13: row_3[2]=23: command not found
Upvotes: 2
Views: 782
Reputation: 295403
The ideal way to do this with modern (bash 4.3+) syntax is thus:
load_array() {
declare -n _load_array__row=$2
declare -a _load_array__line
_load_array__row=( )
while read -r -a _load_array__line; do
_load_array__row+=( "${_load_array__line[@]}" )
done <"$1"
}
(The variable names are odd to reduce the chance of collisions with the calling function; the other answers you're given will have trouble if asked to load content into a variable named ROW
or line
, for instance, as they'll be referring to local variables rather than global ones in describing their destinations).
A similar mechanism compatible with bash 3.2 (the ancient release shipped by Apple), avoiding the performance hit associated with inner loops and the bugs associated with glob expansion (see what your original code does to a line containing *
!) follows:
load_array() {
local -a _load_array__array=( )
local -a _load_array__line
while read -r -a _load_array__line; do
_load_array__array+=( "${_load_array__line[@]}" )
done <"$1"
eval "$2=( \"\${_load_array__array[@]}\" )"
}
Upvotes: 1
Reputation: 107759
In the assignment syntax, what is to the left of the equal sign must be either a variable name (when assigning to a scalar), or a variable name followed by a word in square brackets (when assigning to an array element). In your code, $ROW[$i]=$word
doesn't match this syntax (there's a $
at the beginning, so it can't possibly be an assignment); it's just a word that happens to contain the character =
.
In bash, you can use the declare
builtin to assign to a variable whose name is the result of some computation such as a variable expansion. Note that unlike for a straight assignment, you do need double quotes around the value part to prevent word splitting and filename expansion on $word
. Pass the option -g
if you're doing the assignment in a function and you want the value to remain after the function returns.
declare -g $ROW[$i]="$word"
If you're running an old version of bash that doesn't have declare -g
, you can use eval
instead. This would be the method to use to assign to a dynamically-named variable in plain sh. Take care of quoting things properly: if ROW
is e.g. row_1
, then the string passed as the argument to eval
must be row_1[$i]=$word
(the shell command to parse and execute).
eval "$ROW[\$i]=\$word"
Upvotes: 1