1

I’m trying to center-align a column using the command column.

The output should look like this:

temperatures  
    50  
    12  
    89  
    63  

How can I do this?

Kusalananda
  • 320,670
  • 36
  • 633
  • 936
billy klarr
  • 69
  • 1
  • 5

2 Answers2

2

I wrote this answer before noticing that you want to use the column command, but if that isn’t a requirement, you can center text with this bash shell script:

#!/bin/bash
set -e    # exit the script if something goes wrong

width=12
data="temperatures 50 12 89 63"

for word in $data; do
    strlen=$(printf $word | wc -c)
    w=$(( ($width - $strlen)/2 ))
    printf "%${w}s%s\n" "" $word
done

How it works:

  • $width is the column width
  • The for loop prints every word in $data
  • $strlen is the length of the current word, in bytes.
  • $w is the number of spaces to print before the word to make it centered.
  • printf prints $w spaces, followed by the word

Output:

temperatures
     50
     12
     89
     63

This script will only work for single-byte text encodings. You would need to change the strlen line to handle text containing a , for example.

yellowantphil
  • 897
  • 2
  • 8
  • 20
  • In bash for singlebyte you don't need wc, just `printf "%$(( (width-${#word})/2 ))s%s\n" "$word"` or `printf "%*s\n" $(( (width-${#word})/2 )) "$word"` (quoting of `"$word"` not needed for the specific data here, but good practice in general) – dave_thompson_085 Sep 25 '20 at 02:54
2

Given some file,

temps,temperatures,heatness
500,50,50
1212,12,12
899,89,8989
63,6363,63

the column command could give you aligned columns, but no other special adjustment of them:

$ column -t -s, file
temps         temperatures  heatness
500           50            50
1212          12            12
899           89            8989
63            6363          63

With a small-ish awk script, we can do centering:

$ awk -F, -f center-cols.awk file
temps temperatures heatness
 500       50         50
1212       12         12
 899       89        8989
 63       6363        63
$ awk -F, -v OFS='|' -f center-cols.awk file
temps|temperatures|heatness
 500 |     50     |   50
1212 |     12     |   12
 899 |     89     |  8989
 63  |    6363    |   63

The center-cols.awk script might look like

function center(w, string,         space, before, after) {
    space  = w - length(string)  # number of spaces in total to fill
    before = int(space/2)        # spaces before string
    after  = space - before      # the rest of spaces after
    return sprintf("%*s%s%*s", before, "", string, after, "")
}

FNR == 1 {
    for (i = 1; i <= NF; ++i)
        width[i] = length($i)
}

{
    for (i = 1; i <= NF; ++i)
        $i = center(width[i], $i)

    print
}

The script centers each column in the data according to the width of the header column (the first row) of each row. This means that rows with data that is longer than the header will still be misaligned. This restriction simplifies the script's implementation (one would otherwise have to parse the file twice, once to find the maximum column width for each column, and once to reformat the data).

The script first gets the width of each header column and saves these into the width array. It then rewrites each column using the center function.

The center function takes a width and a string, and returns a string centered within that width, with flanking spaces filling the unused space. The resulting string is constructed using sprintf() with which the flanking spaces are written using the %*s format specifier. This specifier takes a width and a string, and we give it the calculated width and an empty string for both the space at the start and the back of the actual data (the empty string would be formatted to the given width).

Once all columns of a row has been reformatted, the line is printed.


By parsing the file twice, we can find the maximum width of each column. This way we don't have to rely on the header row defining the width of tho columns:

function center(w, string,         space, before, after) {
    space  = w - length(string)  # number of spaces in total to fill
    before = int(space/2)        # spaces before string
    after  = space - before      # the rest of spaces after
    return sprintf("%*s%s%*s", before, "", string, after, "")
}

NR == 1 {
    for (i = 1; i <= NF; ++i)
        width[i] = length($i)
    next
}

FNR == NR {
    for (i = 1; i <= NF; ++i)
        if (width[i] < length($i))
            width[i] = length($i)
    next
}

{
    for (i = 1; i <= NF; ++i)
        $i = center(width[i], $i)

    print
}

Running this on a file with short column headers:

$ awk -F, -v OFS='|' -f center-cols.awk file file
 t  | a  | b
500 | 50 | 50
1212| 12 | 12
899 | 89 |8989
 63 |6363| 63

Note that we need to give the filename twice on the command line.

Kusalananda
  • 320,670
  • 36
  • 633
  • 936