I am a big fan of one-liners using sed awk perl and other tools. But there are that are things hard to do in one-liner, such as when you working with a CSV file and there are commas between quotes, or when you want to print a centralized field with printf.A few months ago I wrote ftable more for fun than anything else, but last weekend I took it seriously and created a GitHub repository and a tutorial for it. ftable tutorialftable codeQuestionsDo you know of a tool that's similar to ftable? I hate feeling like re-inventing the wheel.As I am not programmer (I am sysadmin/devops).  Is there anybody willing to review the code and spot my endless mistakes?#! /usr/bin/env perl# Author: Tiago Lopo Da Silva# Date: 20/10/2013# Purpose: Print formatted tableuse strict;use warnings;use POSIX;use Switch;use Getopt::Long qw(:config no_ignore_case);use Data::Dumper;our $comma=<comma>;our $dollar=<dollar>;our $pipe=|;our $plus=+;our $minus=-;our $FS=',';our $nb=0;my %h;if ($#ARGV >= 0){my $lf; my $cf; my $rf; my $print;GetOptions( 'l|left:s' => \$lf,             'r|right:s' => \$rf,            'c|center:s' => \$cf,                'p|print:s' => \$print,            'F:s' => \$FS,                'n|noborder' => \$nb,           ) || print_usage();%h=get_details($lf,$cf,$rf,$print);}else {%h=get_details();}print_table(\%h);sub get_quoted_fields {# this sub finds quoted fields    my $str1 = $_[0];    my $qf;    while ( $str1 =~ /(['].*?['])/ ){        $qf.=$1${comma};        $str1 =~ s/$1//;    }    return $qf;}sub get_translated {    my $qf = $_[0];    my $str= $_[1];    my @arr;    my %h;    if (defined ($qf)) {        @arr = split(/$comma/,$qf);    }    foreach my $i ( @arr ){        my $tmpvar=$i;        $i =~ s/$FS/$comma/g;        $h{$tmpvar} = $i;    }    while ( my($key,$value) = each(%h) ){        $key =~ s/\$/\\\$/g;        eval \$str =~ s/$key/$value/g; ;    }    return $str;}sub special_split {#This sub splits strings but taking quoted fields in consideration    my $str=$_[0];    $str =~ s/\$/$dollar/g;    $str =~ s/\(/<op>/g;    $str =~ s/\)/<cp>/g;    $str =~ s/\//<slash>/g;    my $str1=$str;    my $qf;    my @a;    $qf=get_quoted_fields($str1);    my $translated = get_translated($qf,$str);    @a = split (/$FS/,$translated);    foreach my $i ( @a ){my $safe_fs=$FS;switch($safe_fs) {case '\.' {$safe_fs =~ s/\\//g;}case '\t' {$safe_fs =~ s/\\t/\t/g;}case '\s' {$safe_fs =~ s/\\s/ /g;}}            $i =~ s/$comma/$safe_fs/eg;            $i =~ s/$dollar/\$/g;            $i =~ s/<op>/\(/g;            $i =~ s/<cp>/\)/g;            $i =~ s/<slash>/\//g;            $i =~ s/[']//g;            $i =~ s/\s+/ /g;    }    return @a;}sub fill_str {# This sub fills the string with padding charsmy $f_char=$_[0];my $f_times=$_[1];my $str;$str=$f_charx$f_times;return $str;}sub print_border {# This sub prints horizontal bordermy @length=@{$_[0]};foreach my $i (@length){unless (defined($i)){$i=1;}print $plus;my $counter=0;while ( $counter < ($i+2) ){print $minus;$counter++;}}print $pipe\n;}sub print_left {#This sub prints fields with proper padding in the left side.#It takes two args, 1st length of the maximun field and field content.my $length=$_[0];my $col=$_[1];unless (defined($length)){ $length=;}unless (defined($col)){ $col=;}my $str=printf ' %-.$length.s ','.$col.';;eval $str}sub print_right {#This sub prints fields with proper padding in the right side.#It takes two args, 1st length of the maximun field and field content.my $length=$_[0];my $col=$_[1];unless (defined($length)){ $length=;}unless (defined($col)){ $col=;}my $str=printf ' %.$length.s ','.$col.';;eval $str}sub print_center {#This sub prints fields with proper padding in the both sides.#It takes two args, 1st length of the maximun field and field content.my $length=$_[0];my $col=$_[1];my $str;unless (defined($length)){ $length=1}my $cl=length($col);my $padding=(($length - $cl)/2);        my $lp; my $rp;if ( (($length - $cl) % 2 ) == 0 ){$lp=$padding;   $rp=$padding;   }else{$lp=ceil($padding); $rp=floor($padding);    }my $l_str ; my $r_str;$l_str=fill_str( ,$lp);$r_str=fill_str( ,$rp);$str=printf ' %.$length.s ','.$l_str.$col.$r_str.';;eval $str;}sub get_details {# This subs creates a hash containg the whole content of the table, alignment info and# number of columns/ fieldsmy @align = get_align($_[0],$_[1],$_[2]);my @print = get_print($_[3]);my @content;my @tmp_arr;my @tmp_arr2;my @length;my $n_col=0;my $counter=0;my $p_print;if(@print){ $p_print=1; }else{ $p_print=0;}while (<>){@tmp_arr= special_split($_);unless( $p_print ){for ( my $i=0 ; $i <= $#tmp_arr; $i++){$print[$i]=$i;  }}my $counter2=0;foreach my $i (@print){$tmp_arr2[$counter2] = $tmp_arr[$i];$counter2++}$counter2=0;foreach my $i (@tmp_arr2){defined($i) && $i =~ s/^\s+//;defined($i) && $i =~ s/\s+$//;$content[$counter][$counter2] = $i;my $li= length($i);if ( defined( $length[$counter2] ) ){if( $li > $length[$counter2] ) {$length[$counter2]=$li;}}else{$length[$counter2]=$li;}   $counter2++;}if ( $counter2 > $n_col ){ $n_col=$counter2;}$counter++;}my %details= (content => \@content, # content of the filelength => \@length, # Maximun length of fieldsalign => \@align, # Alignmentn_col => $n_col, # Maximun number of columns/fields);return %details;}sub print_table{my %h = %{$_[0]};my @content=@{$h{content}};my @length=@{$h{length}};my @align=@{$h{align}};my $n_col=$h{n_col};my $counter=0;foreach my $line (@content){$nb || print_border(\@length);my $str;my $counter2=0;for ( my $i=0; $i < $n_col ; $i++ ){my $col = $content[$counter][$i];unless (defined($col)) { $col = }$col =~ s///g;$col =~ s/'//g;my $l=$length[$counter2];$nb || print $pipe;my $left=false; my $right=false;my $center=false;switch ($align[$counter2]){case l { $left=true;}case r { $right=true;}else { $center=true;}}if ( $right eq true ){print_right($l,$col);}if ( $left eq true ){print_left($l,$col);}if ( $center eq true ){print_center($l,$col);}$counter2++;}unless ($nb) {print $pipe\n}else{print \n}$counter++;}$nb || print_border(\@length);}sub get_align {# This sub creates an array with the alignment informationmy $lf = $_[0];my $cf = $_[1];my $rf = $_[2];my @align;defined($lf) && (my @lf = split (/,/,$lf));defined($cf) && (my @cf = split (/,/,$cf));defined($rf) && (my @rf = split (/,/,$rf));foreach my $i (@lf){$align[$i] = l;   }foreach my $i (@cf){$align[$i] = c;   }foreach my $i (@rf){$align[$i] = r;   }shift(@align);return @align;}sub get_print {# This sub creates an array containing the field numbers to be printedmy $print = $_[0];my @print;defined($print) && (my @a = split(/,/,$print));my $counter=1;foreach my $i (@a){$print[$counter] = $i-1;$counter++;}shift(@print);return @print;}sub print_usage {my $usage = << 'EOF';Usage: ftable [OPTIONS] [FILE]Options:  -l, --left        List of field numbers (separated by comma) to be left aligned  -r, --right        List of field numbers (separated by comma) to be right aligned  -c, --center        List of field numbers (separated by comma) to be center aligned        It is default if no alignmnet provided  -p, --print        List of field numbers (separated by comma) to be printed and ordered  -n, --noborder        Do not print border  -F, --field-separator        Field separator, if no specified comma (,) is the default valueExamples:        ftable -F ':' -p 3,1,6 /etc/passwd        ftable -l 1 -c 2,3 -r 4 /tmp/table.csv        ftable -n -F ':' /etc/passwdEOFprint $usage;exit 2;}
Command line tools to format tables
perl;csv;formatting