13

I wanted to display a long list of strings from an array.

Right now, my script run through a for loop echoing each value to the standard output:

for value in ${values[@]}
do
  echo $value
done

Yeah, that's pretty ugly! And the one column listing is pretty long too...

I was wondering if i can find a command or builtin helping me to display all those values in columns, like the ls command does by default when listing a directory (ls -C).

[Update]

Losing my brain with column not displaying properly formatted columns, here's more info:

The values:

$ values=( 01----7 02----7 03-----8 04----7 05-----8 06-----8 07-----8 08-----8 09---6 10----7 11----7 12----7 13----7 14-----8 15-----8 16----7 17----7 18---6 19-----8 20-----8 21-----8)

Notice the first two digits as an index and the last one indicating the string length for readability.

The command: echo " ${values[@]/%/$'\n'}" | column

The result: bad columns http://tychostudios.ch/multipurpose/bad_columns.png

Something is going wrong...

6
  • column seems to be struggling with the facts that your input has one column and some rows are narrower than a tab-stop. echo " ${values[@]/%/$' \n'}" | column seems to work Commented Jun 13, 2010 at 22:40
  • @Johnsyweb: I had to add another space before \n to make it work with that set of data. But cuuriously, the output is on 3 columns only. Although there is plenty room for 5 columns. Commented Jun 14, 2010 at 7:02
  • @Arko: SO.com swallowed my double-space before the \n! echo " ${values[@]/%/$' \n'}" | column -c 120 provides five columns for me. Commented Jun 15, 2010 at 9:32
  • Again, SO halved my spaces before the newline! Commented Jun 15, 2010 at 9:33
  • 1
    @Johnsyweb: In fact, SO didn't swallowed your spaces. It is an HTML feature, multiple white spaces are rendered as one by default. Look at the source, there they are. Commented Jun 15, 2010 at 14:15

7 Answers 7

27

You could pipe your output to column.

column seems to struggle with some data in a single-column input being narrower than a tabstop (8 characters).

Using printf within a for-loop to pad values to 8 characters seems to do the trick:

for value in "${values[@]}"; do 
    printf "%-8s\n" "${value}"
done | column
Sign up to request clarification or add additional context in comments.

6 Comments

+1 Correct! column for column-wise output and column -x for row-wise output.
and @Dennis: Nice one! That's exactly what i was looking for. But the result is not perfect: depending on the strings lenght, there's sometimes holes in the columns. It feels like it snaps the columns size at some width (like tabs inserted between values) but does not take care of the largest value lenght to compute the column overall width. The ls command does take care of this for a clean output.
printf "%-8s\n" "${value[@]}" | column also works.
@DennisWilliamson I'm stuck with column -x. How to specify the number of columns? I've tried env COLUMNS=4 <command> | column -x, but the number of printed columns is still not 4.
@pmor: column divides the terminal width based on the widths of the elements rather than specifying a number of columns. You can try setting the -c option to different values. There are a lot of table options in the GNU version which I haven't tried. Take a look and see if any do what you want. Those options aren't included in the MacOS/BSD version.
|
5

Here are a couple of techniques that can be used with Johnsyweb's answer so you can do your output without a loop:

saveIFS=$IFS
IFS=$'\n'
echo "${values[*]}" | column
IFS=$saveIFS

or

echo " ${arr[@]/%/$'\n'}" | column

or

echo " ${arr[@]/%/$'\n'}" | sed 's/^ //' | column

5 Comments

Nice! Tried all three techniques, roughly same result. See my comment on @Johnysweb's answer.
@Arko: Without seeing your data it's impossible to make any further suggestions. columns does use tabs and it also adjusts the number of columns based on the length of the longest string.
My data is a simple array of strings, like `values=(one two three four five)'. I must have missed something. No way to get a properly formatted column. Will update question with more info about it.
@Arko: Your data lines up perfectly for me using the command you supplied. I was not able to reproduce exactly what your screenshot shows, but I came close by narrowing my terminal window to 60 characters wide and issuing a tabs -10 command before issuing your echo ... column command.
I was expecting your answer. The problem is specific to my computer. I'll try to track down why. In the mean time, i found an other "way" to do it... See my own answer. ;)
2

It may look overkill, but i came up with my own solution. I wrote a little script that does exactly what i wanted: take a list of values and output them in a pretty formatted column view.

http://github.com/Arko/Columnize

3 Comments

Suggestions: integer comparison: if (( ${#value} > $longest_value )) and others, you use (()) elsewhere - you can here, too: ((columns = term_width / (longest_value + 2) )) and ((spaces_missing = longest_value - value_len + 2 )), instead of echoing spaces in a for loop: printf "%*s" $spaces_missing
@Dennis Williamson: Changes committed! I knew that for loop was awkward... Thank you for the suggestions! :-)
This is a great solution! All the other options didn't account for the screen width and fit as many columns as it could. Thanks!
2

I know this is an old thread, but I get erratic results from column so I thought I'd give my solution. I wanted to group every 3 lines into 3 evenly spaced columns.

cat file.txt | xargs printf '%-24s\n' | sed '$p;N;s/\n//;$p;N;s/\n//'

Basically, it pipes each line into printf, which left-aligns it into a 24-character-wide block of whitespace, which is then piped into sed. sed will look ahead 2 lines into the future and remove the line break.

The N command reads the next line into the current buffer, and s/\n// removes the line break. In short, what we want is 'N;N;s/\n//g', which will work in some cases. The problem is, if there aren't two extra lines to throw in the buffer, sed will quit suddenly. The $p commands pre-empt that by saying "If this is the last line, print the buffer contents immediately".

2 Comments

This is a different scenario from the question. You should probably post it as another question.
This is a great answer to the question I had. I needed to output columns without using column.
1

For some reason column doesn't work for me. In my case I have an array like

$ array = ( 1 2 3 dir1 dir2 dir3 )
$
$ echo ${array[@]} | column            
1 2 3 dir1 dir2 dir3

instead I used

$ echo ${array[@]} | tr ' ' '\n' | sort
1
2
3
dir1
dir2
dir3

1 Comment

Try: IFS=$'\n' echo "${array[@]}" | column
1

I know it's an old thread but I ran into this same situation. I ended up combining a few ideas from here.

For the column utility based solution (without initial space issue):

(IFS=''; echo "${values[*]/%/$'\n'}") | column

Using @Arko's script for basis, this can handle values with spaces. Notice the printf spec of %-*s for left justified colums and right would drop the '-'. I didn't include my terminal guessing but it is settable (terminalWidth).

#  e.g.  column "$@"
#        spacing=4 column "$@"
#        left=6 spacing=3 column "$@"
#
function column() {
    local leftMargin=${left:-0}
    local padding=${spacing:-2}
    local maxWidth=$(( ${terminalWidth:-80} - $leftMargin ))
    local values=("$@")
    local max=$(( $(IFS=''; echo "${values[*]/%/$'\n'}" | wc -L) + $padding ))
    local cols=$(( $maxWidth / $max ))

    local pos=0 NL=''
    for value in "${values[@]}"; do
        [[ $pos == 0 ]] && {
            printf "$NL%*s" "$leftMargin"
            NL=$'\n'  # add newlines from now on...
        }
        printf "%-*s" "$max" "$value"
        (( pos = ($pos + 1) % $cols ))
    done
    echo
}    

Comments

0

Tabspaces


Use a tab after each value.

( It automatically flows to next line too... )

for value in ${values[@]}
do
  echo -en "$value\t"
done

Detailed info here

GoodLUCK!!
- CVS

1 Comment

I'm pretty sure you mean echo -en "$value\t"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.