0

Noble StackOverflow readers,

I have a comma seperated file, each line of which I am putting into an array. Data looks as so...

25455410,GROU,AJAXa,GROU1435804437
25455410,AING,EXS3d,AING4746464646
25455413,TRAD,DLGl,TRAD7176202067

There are 103 lines and I am able to generate the 103 arrays without issue.

n=1; while read -r OrdLine; do
    IFS=',' read -a OrdLineArr${n} <<< "$OrdLine"
    let n++
done < $WkOrdsFile

HOWEVER, I can only access the arrays as so...

echo "${OrdLineArr3[0]}  <---Gives 25455413

I cannot access it with the number 1-103 as a variable - for example the following doesn't work...

i=3
echo "${OrdLineArr${i}[0]}

That results in...

./script2.sh: line 24: ${OrdLineArr${i}[0]}: bad substitution

I think that the answer might involve 'eval' but I cannot seem to find a fitting example to borrow. If somebody can fix this then the above code makes for a very easy to handle 2d array replacement in bash!

Thanks so much for you help in advance!

Dan

  • 1
    Possibly duplicate of http://stackoverflow.com/questions/6724056/nested-shell-parameter-expansion. – Chandranshu Nov 17 '13 at 01:22
  • Bash supports only one-dimensional arrays and you would be better off using Perl for this task. If you really have to use Bash check out http://tldp.org/LDP/abs/html/arrays.html specifically example 27.17 – MattSizzle Nov 17 '13 at 01:28
  • @ruakh - the indirect expansion within a function worked a treat. Is a very nice paradigm for 2-d arrays when forced to use bash to process them! Thanks so much! – user3000596 Nov 20 '13 at 11:30

3 Answers3

1

You can use indirect expansion. For example, if $key is OrdLineArr4[7], then ${!key} (with an exclamation point) means ${OrdLineArr4[7]}. (See §3.5.3 "Shell Parameter Expansion" in the Bash Reference Manual, though admittedly that passage doesn't really explain how indirect expansion interacts with arrays.)

I'd recommend wrapping this in a function:

function OrdLineArr () {
    local -i i="$1"          # line number (1-103)
    local -i j="$2"          # field number (0-3)
    local key="OrdLineArr$i[$j]"
    echo "${!key}"
}

Then you can write:

echo "$(OrdLineArr 3 0)"        # prints 25455413
i=3
echo "$(OrdLineArr $i 0)"       # prints 25455413

This obviously isn't a total replacement for two-dimensional arrays, but it will accomplish what you need. Without using eval.

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • This is definitely the best answer, yet, the `echo` statement at the end of the function `OrdLineArr` will remove trailing newlines if any. If this is a concern, use a global variable for the return value of that function, e.g., `OrdLineArr_ret=${!key}`. – gniourf_gniourf Nov 17 '13 at 18:16
  • Another quick comment, you can have access to the full array with `aryp=OrdLineArr[@]`: then `"${!aryp}"` will expand to the full array. – gniourf_gniourf Nov 17 '13 at 18:16
  • @gniourf_gniourf: Re: trailing newlines: Yeah, I thought about that (it's one of the reasons I don't consider this a total replacement for two-dimensional arrays), but the OP's array obviously will never have newlines in its elements, so I considered it O.K. – ruakh Nov 17 '13 at 18:47
  • @gniourf_gniourf: By the way, it's actually not the `echo` that removes the trailing newlines, but rather the command-substitution that I wrapped `OrdLineArr` in when I called it. If `${OrdLineArr3[0]}` ends in a sequence of newlines, then `OrdLineArr 3 0` will properly print those newlines, but `echo "$(OrdLineArr 3 0)"` will discard them. (That's a bit pedantic, but it means that a sufficiently motivated caller could work around it by writing something like `foo="$(OrdLineArr 3 1 ; echo .)" ; foo="${foo%.}"`.) – ruakh Nov 17 '13 at 18:57
  • If the goal is to put it in a variable then the motivated caller will discard this function altogether and instead create one that takes a variable name as an extra parameter and will return the value in this variable with `printf -v`. – gniourf_gniourf Nov 17 '13 at 19:04
0

eval is usually a bad idea, but you can do it with:

eval echo "\${OrdLineArr$i[0]}"
William Pursell
  • 204,365
  • 48
  • 270
  • 300
0

I would store each line in an array, but split it on demand:

readarray OrdLineArr < $WkOrdsFile
...
OrdLine=${OrdLineArr[i]}
IFS=, read -a Ord <<< "$OrdLine"

However, bash isn't really equipped for data processing; it's designed to facilitate process and file management. You should consider using a different language.

chepner
  • 497,756
  • 71
  • 530
  • 681