292

How do I print the array element of a Bash array on separate lines? This one works, but surely there is a better way:

$ my_array=(one two three)
$ for i in ${my_array[@]}; do echo $i; done
one
two
three

Tried this one but it did not work:

$ IFS=$'\n' echo ${my_array[*]}
one two three
jww
  • 90,984
  • 81
  • 374
  • 818
Axel Bregnsbo
  • 3,403
  • 2
  • 20
  • 15

7 Answers7

558

Try doing this :

$ printf '%s\n' "${my_array[@]}"

The difference between $@ and $*:

  • Unquoted, the results are unspecified. In Bash, both expand to separate args and then wordsplit and globbed.

  • Quoted, "$@" expands each element as a separate argument, while "$*" expands to the args merged into one argument: "$1c$2c..." (where c is the first char of IFS).

You almost always want "$@". Same goes for "${arr[@]}".

Always quote them!

Zombo
  • 1
  • 55
  • 342
  • 375
Gilles Quenot
  • 154,891
  • 35
  • 213
  • 206
  • 6
    And note, the double quotes around the variable reference are important if you want to make sure elements with internal spaces aren't inadvertently split up. – danfuzz Mar 28 '13 at 20:59
  • 4
    @sputnick: does not work, the array elements end up on a single line – Axel Bregnsbo Mar 28 '13 at 21:02
  • Are you sure you used `[@]` and not `[*]`? It makes a difference. – danfuzz Mar 28 '13 at 21:08
  • @danfuzz: you are right I used `[*]` and not `[@]`. It works with `[@]`, though I am puzzled about my mistake: pretty sure I copy-pasted directly from sputnicks post. – Axel Bregnsbo Mar 28 '13 at 21:15
  • 1
    What are the two hyphens after the command for? I do not found any references to it in the manual. – joanpau Jul 15 '14 at 13:10
  • 1
    It means : 'end of arguments'. – Gilles Quenot Jul 22 '14 at 23:21
  • This solution didn't work for me, despite using an identical command. – pfff Dec 30 '14 at 16:28
  • 1
    'didn't work' without explanations and context is like talking to a rock and expect it to solve your problem. – Gilles Quenot Dec 30 '14 at 16:46
  • @sputnick I assumed it's a given that the context is a bash script being run in an environment that supports bash and printf, a valid array with more than one element. Maybe I should add that the array elements are all absolute paths to files? But that shouldn't matter because they're treated as strings. – pfff Dec 31 '14 at 20:52
  • 4
    Is there a way to make it so that it outputs no blank lines if there are no elements in the array without having to `| grep -v '^$'`? – Noel Yap Jan 15 '15 at 23:18
  • `[[ ${arr[@]} ]] && printf '%s\n' "${arr[@]}"` – Gilles Quenot Jan 16 '15 at 05:30
  • how about two arrays? – Raymond gsh Oct 07 '15 at 16:52
  • 1
    what is "printf '%s\n' " for? – espaciomore Feb 07 '17 at 13:08
  • 3
    @espaciomore '%s\n' is the format for the printf function's output. %s means a placeholder for a string argument (in this case the array element) and \n adds a line break after that. Thus, there will be a string and a line break in the output for each element in the array. – Koja Apr 13 '17 at 07:49
  • `a=( 1 2 3 ); echo "${a[@]}"` prints `1 2 3`. I'm using bash. Why doesn't it work for me? – stackoverflowed Feb 01 '19 at 08:06
86

Just quote the argument to echo:

( IFS=$'\n'; echo "${my_array[*]}" )

the sub shell helps restoring the IFS after use

perreal
  • 90,214
  • 20
  • 145
  • 172
  • 3
    sorry perreal, I moved my check mark to sputnick, despite liking your solution better, just because I learned about the 'printf' function. – Axel Bregnsbo Mar 28 '13 at 21:25
  • 3
    Thanks for this answer - I like it! Too bad assignments happen after expansion so `IFS=$'\n' echo "${my_array[*]}"` doesn't work. Oh well! – cxw Dec 14 '16 at 13:08
  • @cxw, what do you mean by "assignments happen"? – Steven Shaw Nov 22 '18 at 22:59
  • 1
    @bschlueter, I tried with Bash 4 — 4.4.23(1)-release — and it works! – Steven Shaw Nov 22 '18 at 23:00
  • @StevenShaw [this](https://www.gnu.org/software/bash/manual/html_node/Simple-Command-Expansion.html) - in my example, bash saves the `IFS=...` (linked #1), then expands `"${my_array[*]}"` (#2), _then_ changes `IFS` (#4). As a result, you can't do this in one command - you need the IFS change first, so you need a subshell `( ... )` unless you are willing to change IFS in your current shell. – cxw Nov 22 '18 at 23:12
  • 1
    @cxw Ah, I didn't see what you were trying to do there. I think it doesn't work because `echo` is a builtin in Bash. However, you can wrap it in a function and it will work! https://gist.github.com/steshaw/53ba0095bce8ccab52d26a14375dedb8 – Steven Shaw Nov 24 '18 at 02:05
  • I do like the `printf` solution though. I hadn't realised that, as the Bash manual points out, "The format is reused as necessary to consume all of the arguments". It's a bit counterintuitive for C programmers, however . – Steven Shaw Nov 24 '18 at 02:12
  • @StevenShaw good idea - I hadn't thought of using a function. It's not because `echo` is a builtin, though. It's because the function in your gist includes the expansion, so the expansion doesn't happen until after the function is called, by which point `IFS` has already been changed. – cxw Nov 24 '18 at 12:57
  • @cxw, Bash is a bit nasty, isn't it? – Steven Shaw Nov 24 '18 at 22:03
53

Using for:

for each in "${alpha[@]}"
do
  echo "$each"
done

Using history; note this will fail if your values contain !:

history -p "${alpha[@]}"

Using basename; note this will fail if your values contain /:

basename -a "${alpha[@]}"

Using shuf; note that results might not come out in order:

shuf -e "${alpha[@]}"
Zombo
  • 1
  • 55
  • 342
  • 375
8

Another useful variant is pipe to tr:

echo "${my_array[@]}" | tr ' ' '\n'

This looks simple and compact

Steven Shaw
  • 5,753
  • 2
  • 31
  • 42
0x00
  • 187
  • 1
  • 4
3

I tried the answers here in a giant for...if loop, but didn't get any joy - so I did it like this, maybe messy but did the job:

 # EXP_LIST2 is iterated    
 # imagine a for loop
     EXP_LIST="List item"    
     EXP_LIST2="$EXP_LIST2 \n $EXP_LIST"
 done 
 echo -e $EXP_LIST2

although that added a space to the list, which is fine - I wanted it indented a bit. Also presume the "\n" could be printed in the original $EP_LIST.

wuxmedia
  • 399
  • 3
  • 6
1

You could use a Bash C Style For Loop to do what you want.

my_array=(one two three)

for ((i=0; i < ${#my_array[@]}; i++ )); do echo "${my_array[$i]}"; done
one
two
three
0

I've discovered that you can use eval to avoid using a subshell. Thus:

IFS=$'\n' eval 'echo "${my_array[*]}"'