2

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
1

2 Answers 2

1

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"
Sign up to request clarification or add additional context in comments.

5 Comments

It seems like this kind of declare makes only local assignments and doesn't allow to use declared values in global. This is echo of variables i am getting: i.imgur.com/SSlUNWL.png
@user2816626 Ah, sorry, I hadn't noticed you needed the value outside the function. Add the option -g (see my edit).
That is a great solution. I had not seen that before. I would have presumed you would have to have used an eval or a ${!ROW} type construct. Learning has occurred.
Now it says that there is no such option... 'lab3.sh: line 16: declare: -g: invalid option declare: usage: declare [-afFirtx] [-p] [name[=value] ...]'
@user2816626 Oh, you're running a very old version of bash? You should mention that. See my edit.
1

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[@]}\" )"
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.