I have two arrays, which I want to print out in two separate columns from a single printf call. The following:

printf "%-${padding}s→  %s\n" "${lnFrom[@]}" "${lnTo[@]}"


dotfiles/git/gitconfig         →  dotfiles/hyper/hyper.css
dotfiles/hyper/hyper.js        →  dotfiles/nvim/nvimrc
dotfiles/nvim/warm_nature.vim  →  dotfiles/zsh/zshrc
~/.gitconfig                   →  ~/.hyper.css
~/.hyper.js                    →  ~/.nvimrc
~/.vim/colors/warm_nature.vim  →  ~/.zshrc

The three first lines are represent the lnFrom array, and the three last lnTo, they should be on their respective columns.

(padding in the printf statement is just a variable containing the length of the longtest string in lnFrom array)

Thoughts on how to fix this while keeping the single printf call? (not introducing any loops)

1 Answer 1


Your format specifiers don't match up to the number of elements present in each array. Once your array expansion happens, there are multiple words generated each needs a %s specifier to be matched up. So printf is just going to fill them with all of the elements of the first array, followed by all of the elements of the second.

Unless you know the exact number of elements that you are printing from both the arrays combined, you can't hard-code a static format specifier string. One way would be dynamically produce the format specifier array, by tracking the total number of elements that would be produce if both the arrays are expanded

count=$(( ${#lnFrom[@]} + ${#lnTo[@]} ))

and now create the array

format_specifiers=( "%-${padding}s" )
for ((i=1; i<=count-1; i++)); do
    [ "$i" -eq "${#lnFrom[@]}" ] && { format_specifiers+=( "  %s" ); continue; }
    format_specifiers+=( "→  %s" )

and now print the elements as

printf "${format_specifiers[*]}" "${lnFrom[@]}" "${lnTo[@]}"

If you had tested the above, it could contain an additional -> between the part where the first array ends and the next one starts. It is because, we haven't identified the start/end positions of the array while creating the format specifier array.

You could do this

for ((i=1; i<=count-1; i++)); do
    [ "$i" -eq "${#lnFrom[@]}" ] && { format_specifiers+=( "  %s" ); continue; };         
    format_specifiers+=( "→  %s" )

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .