1

I create an array with a comma for delimiter:

echo "${myarray[*]}" # prints: 22,3,2,0,22,4,5,8,22,4,3,6 

I'd like to print it to terminal in chunks of four, with a newline and no comma before newline. Like:

22,3,2,0 22,4,5,8 22,4,3,6 

I've been struggling trying to get for loops to work with it like:

for i in {0..${#myarray[@]}..4} do ##tried lots of things that didn't work here done 

Can someone point me in the right direction or how to think about it?

3
  • research bash array slicing
    – jsotola
    CommentedAug 9, 2024 at 5:39
  • 1
    Sadly, you can't use variables or other expansions within brace expansion in Bash. That's because for whatever reason, Bash expands braces first, and variables only after that, while this would need the opposite order. I.e. {0..$n..4} is not recognized as a brace expansion, and left as-is, and then after variable expansion it gives you {0..123..4}, so nothing you do inside the loop really matters.
    – ilkkachu
    CommentedAug 9, 2024 at 6:21
  • echo "${myarray[*]}" indicates myarray is not an array but rather a single string; please update the question with the complete output from typeset -p myarrayCommentedAug 9, 2024 at 12:31

3 Answers 3

5

If the number of elements is guaranteed to be a multiple of 4 and greater than 0:

$ myarray=(22 3 2 0 22 4 5 8 22 4 3 6) $ printf '%s,%s,%s,%s\n' "${myarray[@]}" 22,3,2,0 22,4,5,8 22,4,3,6 

If not, that gives:

$ myarray=(22 3 2 0 22 4 5 8 22 4 3 6 extra) $ printf '%s,%s,%s,%s\n' "${myarray[@]}" 22,3,2,0 22,4,5,8 22,4,3,6 extra,,, 
$ myarray=() $ printf '%s,%s,%s,%s\n' "${myarray[@]}" ,,, 

With zsh:

myarray=(22 3 2 0 22 4 5 8 22 4 3 6 extra) while (( $#myarray )) { print -r -- ${(j[,])myarray[1,4]} myarray[1,4]=() } 

Which modifies $myarray or:

myarray=(22 3 2 0 22 4 5 8 22 4 3 6 extra) for (( i = 1; i <= $#myarray; i += 4 )) print -r -- ${(j[,])myarray[i,i+3]} 

Which both give:

22,3,2,0 22,4,5,8 22,4,3,6 extra 

That latter one you could do in bash with:

myarray=(22 3 2 0 22 4 5 8 22 4 3 6 extra) (IFS=, for (( i = 0; i < ${#myarray[@]}; i += 4 )) { printf '%s\n' "${myarray[*]:i:4}" } ) 

Relying on "${array[*]}" to join the elements with the first character of $IFS as bash has no equivalent for zsh's join parameter expansion flag.

    3
    $ printf '%s\n' "${myarray[@]}" | paste -d , - - - - 22,3,2,0 22,4,5,8 22,4,3,6 

    This uses printf to produce one line of input to paste per element in your array. The paste utility distributes these lines over four comma-delimited fields per output line.

    There is no need for a separate loop in the shell.

    Whatever is producing your array's elements could replace the printf call, and you could, that way, bypass creating the array completely.

    some-command | paste -d , - - - - 

    Note that some-command can be an arbitrarily complex compound command.

    2
    • Re: per element in your array. Strictly speaking, that's per line in elements of your array, though if there are elements with newlines (or commas) in them, it's not clear what the output should look like.CommentedAug 9, 2024 at 8:00
    • @StéphaneChazelas If you allow me to split hairs, what I said was that it would produce one line per element. Any newline embedded in an array element would not be produced by the printf call, only passed on by it. But I definitely see what you're saying. Such newlines would mess up the paste processing.
      – Kusalananda
      CommentedAug 9, 2024 at 8:06
    2

    How about this:

    #!/usr/bin/env bash myarray=(22 3 2 0 22 4 5 8 22 4 3 6) for ((i=0; i<"${#myarray[@]}"; i+=4)); do echo "${myarray[$i]},${myarray[$i+1]},${myarray[$i+2]},${myarray[$i+3]}" done 
    4
    • By the way, if you know that you want to start every line with 22, and you have that data in some textfile or similar, there are simpler ways (e.g. tr, grep, sed, awk, ...) to get this output than loading them into a bash array.
      – pfnuesel
      CommentedAug 9, 2024 at 5:39
    • nah, the array is made from a bunch of reads and that number will change. thanks so much. (i couldnt figure how to iterate by 4 with that syntax... so i def learned something)CommentedAug 9, 2024 at 5:42
    • 2
      I think you could use echo "${myarray[*]:i:4}" with IFS=, instead of manually listing them. Though modifying IFS has a global effect, but then the original question used "${myarray[*]}" too.
      – ilkkachu
      CommentedAug 9, 2024 at 6:18
    • 3
      Note that, for a numerically-indexed array the index part is an arithmetic expression. Bash allows variables to be used without the $ in an arithmetic expression: echo "${myarray[i]},${myarray[i+1]},${myarray[i+2]},${myarray[i+3]}"CommentedAug 9, 2024 at 13:36

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.