philosophie
philosophie

Reputation: 304

Printing multiple iterated arrays in Bash so they alternate

I have 2 arrays, where artists includes music artist names and songs includes song titles. What I want to do is output each in an alternating manner so that I get array1[1] - array2[1], array1[2] - array2[2]..., or:

Chelsea Wolfe - Mer Pavement - Embassy Row Black Math Horseman - Torment of the Metals ...

Here is what I have now, with example arrays provided. The actual arrays are the result of echoed and processed (with grep) curl command substitutions, so I'm not sure if they contain double quotes, but the output I get is same as the example provided here. [Edit: This is incorrect. As I mentioned in a comment to John1024 below, the actual arrays I'm using have newlines after each element.]

I know this is wrong, and I know why (because I'm telling Bash to "print all elements of artists then all elements of songs"), I just don't know the solution.

artists=("Chelsea Wolfe" "Pavement" "Black Math Horseman" "Marriages" "Chelsea Wolfe")
songs=("Mer" "Embassy Row" "Torment of the Metals" "The Liar" "The Waves have Come")

IFS=$'\n'
for ((i=0;i<"${#artists[@]}";++i));
do
printf "%s\n" "${artists[@]} - ${songs[@]}"
done
[Edit: I removed the useless unassigned `OIFS` variable that was here. See my comment to glenn jackman below.]

Would it make sense to somehow combine them in the alternating way I want, and then have that array print, or is there a simpler solution?

Upvotes: 2

Views: 527

Answers (3)

John1024
John1024

Reputation: 113844

Let's eliminate the IFS commands and make some small changes to the printf command:

artists=("Chelsea Wolfe" "Pavement" "Black Math Horseman" "Marriages" "Chelsea Wolfe")
songs=("Mer" "Embassy Row" "Torment of the Metals" "The Liar" "The Waves have Come")

for ((i=0;i<"${#artists[@]}";++i));
do
    printf "%s - %s\n" "${artists[$i]}" "${songs[$i]}"
done

This produces the output:

Chelsea Wolfe - Mer
Pavement - Embassy Row
Black Math Horseman - Torment of the Metals
Marriages - The Liar
Chelsea Wolfe - The Waves have Come

Alternate output

We can get the columns to line up by changing the printf line to:

printf "%-20s - %s\n" "${artists[$i]}" "${songs[$i]}"

With that change, the output becomes:

Chelsea Wolfe        - Mer
Pavement             - Embassy Row
Black Math Horseman  - Torment of the Metals
Marriages            - The Liar
Chelsea Wolfe        - The Waves have Come

Handling arrays with trailing newlines

Suppose, as per the comments, that the elements have trailing newlines:

artists=($'Chelsea Wolfe\n' $'Pavement\n' $'Black Math Horseman\n' $'Marriages\n' $'Chelsea Wolfe')
songs=($'Mer\n' $'Embassy Row\n' $'Torment of the Metals\n' $'The Liar\n' $'The Waves have Come')

We can remove the those newline characters using bash suffix removal:

for ((i=0;i<"${#artists[@]}";++i));
do
    printf "%s - %s\n" "${artists[$i]%$'\n'}" "${songs[$i]%$'\n'}"
done

The output is still:

Chelsea Wolfe - Mer
Pavement - Embassy Row
Black Math Horseman - Torment of the Metals
Marriages - The Liar
Chelsea Wolfe - The Waves have Come

Upvotes: 2

glenn jackman
glenn jackman

Reputation: 246827

We can do this with a paste and some process substitution:

$ paste <(printf "%s\n" "${artists[@]}") <(printf "%s\n" "${songs[@]}")
Chelsea Wolfe   Mer
Pavement    Embassy Row
Black Math Horseman Torment of the Metals
Marriages   The Liar
Chelsea Wolfe   The Waves have Come

That's a bit messy, let's pretty it up with column:

$ paste <(printf "%s\n" "${artists[@]}") <(printf "%s\n" "${songs[@]}") | column -t -s $'\t' -o " - "
Chelsea Wolfe       - Mer
Pavement            - Embassy Row
Black Math Horseman - Torment of the Metals
Marriages           - The Liar
Chelsea Wolfe       - The Waves have Come

Upvotes: 2

Ed Morton
Ed Morton

Reputation: 203615

The reason you're struggling is that the shell is an environment from which to call tools, not a tool for manipulating text, and so it's extremely difficult to write shell scripts to manipulate text. The guys who invented shell to call UNIX tools also invented the UNIX tool awk for the shell to call to manipulate text so just use it:

$ cat tst.awk
BEGIN {
    FS=","; OFS=" - "

    split("Chelsea Wolfe,Pavement,Black Math Horseman,Marriages,Chelsea Wolfe",artists)
    split("Mer,Embassy Row,Torment of the Metals,The Liar,The Waves have Come",songs)

    for (i=1;i in artists;i++) {
        print artists[i], songs[i]
    }
}

$ awk -f tst.awk
Chelsea Wolfe - Mer
Pavement - Embassy Row
Black Math Horseman - Torment of the Metals
Marriages - The Liar
Chelsea Wolfe - The Waves have Come

Upvotes: 0

Related Questions