1

The first three commands work but the fourth does not. How can I append to an array with a variable in its name?

i=4
eval pim$i=
pim4+=(`date`)

pim$i+=(`date`)

Thanks!

user1837378
  • 171
  • 1
  • 2
  • 9
  • Are you targeting bash 4.3 or newer? If so, there's a shiny new feature targeted just at this use case. :) – Charles Duffy Apr 09 '14 at 03:19
  • Incidentally, this is the topic of BashFAQ #6: http://mywiki.wooledge.org/BashFAQ/006 – Charles Duffy Apr 09 '14 at 03:20
  • I'm trying to go off of this SO response but use a variable variable name: http://stackoverflow.com/a/1951523 – user1837378 Apr 09 '14 at 03:24
  • By the way -- if the second line is meant to initialize the array, `declare -a "pim$i"` would be a better way to do it; `eval pim$i=` isn't actually specifying any type, array or otherwise – Charles Duffy Apr 09 '14 at 03:32
  • Also, how would I get the length of the array with a variable array name? `${#pim4[@]}` works but `${#pim$i[@]}` does not. – user1837378 Apr 09 '14 at 04:03
  • ...with eval, `eval 'pim_count=${#pim'"$i"'[@]}'` will do the trick. The usual caveat about needing to trust your `$i` to not be malicious if you're substituting it into an `eval`'d string applies -- if it's data you're getting from a file, a socket, user input, etc., this would be a bad idea. – Charles Duffy Apr 09 '14 at 04:11

1 Answers1

3

With bash 4.3, there's a feature targeted at just this use case: namerefs, accessed with declare -n. (If you have a modern ksh, they're also available using the nameref built-in)

declare -n pim_cur="pim$i"
pim_cur+=( "$(date)" )

With bash 4.2, you can use printf -v to assign to array elements:

array_len=${#pim4[@]}
printf -v "pim4[$array_len]" %s "$(date)"

Prior to bash 4.2, you may need to use eval; this can be made safe by using printf %q to preprocess your data:

printf -v safe_date '%q' "$(date)"
eval "pim$i+=( $safe_date )" # only safe if you can guarantee $i to only contain
                             # characters valid inside a variable name (such as
                             # numbers)
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • The bash 4.3 answer is along the lines of what I'm looking for but I don't have bash 4.3 and thus no nameref. – user1837378 Apr 09 '14 at 03:28
  • Why are the other two answers unsuitable? – Charles Duffy Apr 09 '14 at 03:28
  • 1
    The last answer helped, thanks Charles! I used: eval "pim$i+=(\`date\`)" which I think does the trick. – user1837378 Apr 09 '14 at 03:31
  • @user1837378, for a date specifically, it might be okay. For anywhere the data could possibly be malicious or unsafe (and that includes filenames), you need to do the `printf %q` hoopla or you risk introducing security bugs. – Charles Duffy Apr 09 '14 at 03:33
  • 1
    @user1837378 ...think of adding a filename to a list using eval when that filename is `/tmp/i am a bad person/$(rm -rf /)`. – Charles Duffy Apr 09 '14 at 03:34