46

Sometimes, I'm getting as an input tab separated list, which is not quite aligned, for instance

var1  var2  var3
var_with_long_name_which_ruins_alignment  var2 var3

Is there an easy way to render them aligned?

var1                                      var2  var3
var_with_long_name_which_ruins_alignment  var2  var3
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Elazar Leibovich
  • 3,131
  • 5
  • 27
  • 28
  • Someone could make a solution based on elastic tabstops: http://nickgravgaard.com/elastictabstops/ – Mikel Feb 20 '11 at 09:24
  • See also: http://www.vim.org/scripts/script.php?script_id=294 and http://vimcasts.org/episodes/aligning-text-with-tabular-vim/ – Mikel Feb 20 '11 at 09:29
  • And a Go implementation: http://golang.org/pkg/tabwriter/ – Mikel Feb 20 '11 at 10:00
  • 16
    Tried piping it to `column -t`? – alex Feb 20 '11 at 11:45
  • @alex, that's what I was looking for. Please post it as an answer and I'll accept that. – Elazar Leibovich Feb 20 '11 at 11:51
  • 8
    Tucked away at the end of Mikel's perl answer is the clincher comment (by Mikel)... *`columns -t`* acts on general whitespace. To work with **tabs only**, use *`column -t -s $'\t'`* – Peter.O Feb 20 '11 at 23:40

7 Answers7

62

So, the answer becomes:

column -t file_name

Note that this splits columns at any whitespace, not just tabs. If you want to split on tabs only, use:

column -t -s $'\t' -n file_name

The -s $'\t' sets the delimiter to tabs only and -n preserves empty columns (adjacent tabs).

P.S.: Just want to point out that the credit goes to Alex as well. The original hint was provided by him as a comment to the question, but was never posted as an answer.

Barun
  • 2,336
  • 20
  • 23
  • I'll wait a bit for Alex to get the credit, I think he deserves it. If he wouldn't answer in a few days I'll accept an answer from somebody else. – Elazar Leibovich Feb 20 '11 at 19:23
  • Sure! I too was unaware of `column` :) – Barun Feb 21 '11 at 05:51
  • 1
    This seems ideal but unfortunately `column` seems to fail when it encounters empty cells. See [this post](http://unix.stackexchange.com/q/29023/21014). Depending on which version of `column` you have, you may be able to specify the `-n` option to correct this. – John J. Camilleri Jul 18 '12 at 07:29
  • 1
    Also, this command will not only split on tabs, but also on "any whitespace". To split just on tabs, use `column -t -s $'\t'`. – Fritz Feb 06 '17 at 12:45
  • @Fritz why we have to use $? – Russell Teapot Dec 15 '20 at 05:34
  • 1
    The `$` before a single quote `'` expands escape sequences in the following string. In this case `'\t'` is a string of two characters: bachslash (ASCII 0x5C) followed by `t` (ASCII 0x74). But `$'\t'` contains only one character: a Tab (ASCII 0x09), because the escape sequence `\t` (which stands for "tab") is expanded to an actual tab character. You can try this using hexdump: Try `echo -n '\t' | hd` vs. `echo -n $'\t' | hd`. – Fritz Dec 15 '20 at 11:05
4

For manual tab stops: expand -t 42,48

For automatic tab stops, as suggested by alex: column -t

(expand is on all POSIX systems. column is a BSD utility, available in many Linux distributions as well, thanks to util-linux.)

Cristian Ciupitu
  • 2,430
  • 1
  • 22
  • 29
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
2

Following on from Peter.O's comment which is what I wanted to align (tab delimited data, TSV), this phrase works very nicely:

column -t -s $'\t' /Users/me/data.csv | less --chop-long-lines
Sridhar Sarnobat
  • 1,692
  • 18
  • 27
2

Here's a script to do it:

aligntabs.pl

#!/usr/bin/perl

my $delim = '\s*\t\s*';

my %length = ();
my @lines = ();
for my $line (<>) {
    chomp $line;
    my @words = split $delim, $line;
    my $numwords = scalar(@words);
    for my $i (0..$numwords-1) {
        my $maxlen = $length{$i} // 0;
        my $thislen = length($words[$i]);
        $maxlen = ($thislen > $maxlen)? $thislen: $maxlen;
        $length{$i} = $maxlen;
    }
    push @lines, [@words];
}

foreach my $wordsref (@lines) {
    my @words = @$wordsref;
    my $numwords = scalar(@words);
    for my $i (0..$numwords-1) {
        if ($i < $numwords-1) {
            my $fieldlen = $length{$i};
            printf "%-${fieldlen}s ", $words[$i];
        }
        else {
            print $words[$i];
        }
    }
    print "\n";
}

usage

$ aligntabs.pl < infile
var1                                     var2 var3
var_with_long_name_which_ruins_alignment var2 var3
Mikel
  • 56,387
  • 13
  • 130
  • 149
1
sed 's/||/| |/g;s/||/| |/g' filename-here | column -s"|" -t | less -#2 -N -S

Explanation:

Sed will add a space between blank delimters

Column will add equal spacing between the columns

zydsld|asl|asd
das|aosdk|dd

becomes

zydsld|asl  |asd
das   |aosdk|dd 

Less will open the output in a file viewer. -N and -S will add line number and disable wrapping respectively

Rohit
  • 111
  • 2
1

With Miller (http://johnkerl.org/miller/doc) you have pretty print output.

Run

mlr --inidx --ifs "\t" --opprint cat input | tail -n +2

to have

var1                                     var2 var3
var_with_long_name_which_ruins_alignment var2 var3
aborruso
  • 2,618
  • 10
  • 26
0

Using Miller (mlr) in a slightly different way from what's done in aborruso's answer:

$ mlr --t2p cat file
var1                                     var2 var3
var_with_long_name_which_ruins_alignment var2 var3

This reads the data as a TSV (tab-separated values) file, and simply outputs it in a pretty-printed table form. The --t2p option to mlr is short-hand for --itsv (input TSV) and --opprint (output pretty-printed) together.

With borders:

$ mlr --t2p --barred cat file
+------------------------------------------+------+------+
| var1                                     | var2 | var3 |
+------------------------------------------+------+------+
| var_with_long_name_which_ruins_alignment | var2 | var3 |
+------------------------------------------+------+------+

As a markdown table:

$ mlr --t2m cat file
var1 var2 var3
var_with_long_name_which_ruins_alignment var2 var3
Kusalananda
  • 320,670
  • 36
  • 633
  • 936