84

I'm trying to collect string values in a bash script. What's the simplest way that I can append string values to a list or array structure such that I can echo them out at the end?

Joe
  • 46,419
  • 33
  • 155
  • 245

6 Answers6

145
$ arr=(1 2 3)
$ arr+=(4)
$ echo ${arr[@]}
1 2 3 4

Since Bash uses sparse arrays, you shouldn't use the element count ${#arr} as an index. You can however, get an array of indices like this:

$ indices=(${!arr[@]})
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
19
foo=(a b c)
foo=("${foo[@]}" d)
for i in "${foo[@]}"; do echo "$i" ; done
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
5

To add to what Ignacio has suggested in another answer:

foo=(a b c)
foo=("${foo[@]}" d) # push element 'd'

foo[${#foo[*]}]="e" # push element 'e'

for i in "${foo[@]}"; do echo "$i" ; done
codaddict
  • 445,704
  • 82
  • 492
  • 529
5
$ for i in "string1" "string2" "string3"
> do
> array+=($i)
> done
$ echo ${array[@]}
string1 string2 string3
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
2

The rather obscure syntax for appending to the end of an array in Bash is illustrated by the following example:

myarr[${#myarr[*]}]="$newitem"
Graham Russell
  • 997
  • 13
  • 24
ennuikiller
  • 46,381
  • 14
  • 112
  • 137
  • As Dennis Williamson's answer points out, this is incorrect in some cases; bash arrays are sparse, and the index ${#myarr[*]} may not be the last index. – Evan Krall Jan 07 '10 at 20:35
0

Though the question is answered and is pretty old, I'd like to share a namespace-solution as it works significantly faster than any other ways except for ennukiller's answer (on my 100k lines tests it won ~12 secs against my ~14 secs, whereas list-append solution would take a few minutes).

You can use the following trick:

# WORKS FASTER THAN THESE LAME LISTS! ! !
size=0;while IFS= read -r line; do
    echo $line
    ((++size))
    eval "SWAMP_$size='$line'"
done

Or you can do the following:

#!/bin/bash
size=0
namespace="SWAMP"

ArrayAppend() {
    namespace="$1"
    # suppose array size is global
    new_value="$2"
    eval "${namespace}_$size='$2'"
    eval "echo \$${namespace}_$size"
    ((++size))
}

ArrayAppend "$namespace" "$RANDOM"
ArrayAppend "$namespace" "$RANDOM"
ArrayAppend "$namespace" "$RANDOM"
ArrayAppend "$namespace" "$RANDOM"
ArrayAppend "$namespace" "$RANDOM"

As long as the interpreter is in tag list, here's a link for object oriented bash.

Community
  • 1
  • 1
theoden8
  • 773
  • 7
  • 16
  • Don't use `eval` for assignment (especially on user input)! **your code is subject to code injection and is highly insecure** (and hence broken)! so… it's even lamer than lame lists `:)`. – gniourf_gniourf Sep 20 '15 at 17:39
  • By the way, if your only goal is to slurp your file line by line in an array, with Bash≥4 you should use `mapfile`: `mapfile -t lines < file`. – gniourf_gniourf Sep 20 '15 at 17:42
  • Though, if you ask, I can show you a very little known way of using `eval` safely… – gniourf_gniourf Sep 20 '15 at 17:43
  • @gniourf_gniourf, sure. I'd like to see that. – theoden8 Sep 20 '15 at 18:00
  • This is not the proper way to dynamically assign variables, but here's a safe and little known way: instead of `eval "$a=$b"` use `tmp=$b eval "$a"='$tmp'`. Though there are other better ways: `printf -v "$a" '%s' "$b"` or `declare "$a=$b"`. – gniourf_gniourf Sep 20 '15 at 18:08
  • But hey, the “lame list” is going to be much faster (and safer) if you use the proper tool: `mapfile -t lines < file`. – gniourf_gniourf Sep 20 '15 at 18:11
  • @gniourf_gniourf, whereas lists are widely supported, although not POSIX-kosher, i can't find `mapfile` on my `zsh`. – theoden8 Sep 20 '15 at 19:02
  • 1
    `mapfile` is a Bash builtin (you're in a question tagged [tag:bash]). – gniourf_gniourf Sep 20 '15 at 19:05
  • @gniourf_gniourf, focusing on one implementation is a bad practice, to my mind. But yes, `eval` is an unsafe solution, although it reveals many shell's capabilities. – theoden8 Sep 20 '15 at 19:10