Dinamic Table in Bash (Shell)

73 Views Asked by At

I have a problem, there are a series of commands that generate me data that are stored in the variables $nombreioc, $valorioc and $recursoioc, these are printed in the following table:

+------------+-----------+---------------------+
|    Type    |   Value   | Adicional Resources |
+------------+-----------+---------------------+
| $nombreioc | $valorioc | $recursoioc         |
+------------+-----------+---------------------+

The problem is that the variables have different numbers of characters and the whole table must be adapted to the length of the variable at that moment.

All that I want to do in shellscript, linux

I have tried to use \t with echo, but it does not work

2

There are 2 best solutions below

6
Ed Morton On
$ echo '|Type|Value|Adicional Resources|'; echo '|foo|bar|whatever|'
|Type|Value|Adicional Resources|
|foo|bar|whatever|

$ { echo '|Type|Value|Adicional Resources|'; echo '|foo|bar|whatever|'; } |
column -s'|' -o ' | ' -t
 | Type | Value | Adicional Resources
 | foo  | bar   | whatever

Note the missing | at the end of each line in the column output above, here's the fix for that:

$ { echo '|Type|Value|Adicional Resources| '; echo '|foo|bar|whatever| '; } |
column -s'|' -o ' | ' -t
 | Type | Value | Adicional Resources |
 | foo  | bar   | whatever            |

Note the extra blank I added after the final | on each echoed line above to force column to add the trailing |, otherwise it wouldn't print that nor, more importantly, the padding blanks after the final data value before it on each line.

$ { echo '|Type|Value|Adicional Resources| '; echo '|foo|bar|whatever| '; } |
column -s'|' -o ' | ' -t |
awk '
    { gsub(/^ +| +$/,"") }
    NR == 1 {
        line=$0; gsub(/[^|]/,"-",line); gsub(/[|]/,"+",line)
    }
    NR < 3 { print line }
    { print }
    END { print line }
'
+------+-------+---------------------+
| Type | Value | Adicional Resources |
+------+-------+---------------------+
| foo  | bar   | whatever            |
+------+-------+---------------------+

If you don't have column you could always do it all in awk, e.g. given this input:

$ cat file
Type|Value|Adicional Resources
foo|bar|whatever

here's a solution using any awk reading the input file twice (so will work for input from any size of file but won't work for input coming from a pipe):

$ cat tst.awk
BEGIN { FS="|" }
NR == FNR {
    for ( i=1; i<=NF; i++ ) {
        wid = length($i) + 2
        wids[i] = (wid > wids[i] ? wid : wids[i])
    }
    next
}
FNR == 1 {
    line = "+"
    for ( i=1; i<=NF; i++ ) {
        line = line sprintf("%*s",wids[i],"") "+"
    }
    gsub(/ /,"-",line)
    print line
}
{
    out = FS
    for ( i=1; i<=NF; i++ ) {
        out = out sprintf("%-*s",wids[i]," "$i) FS
    }
    print out
}
FNR == 1 { print line }
END { print line }

$ awk -f tst.awk file file
+------+-------+---------------------+
| Type | Value | Adicional Resources |
+------+-------+---------------------+
| foo  | bar   | whatever            |
+------+-------+---------------------+

or reading the input file once and storing all of the input in memory (so it will work with input from a file or a pipe as long as that input isn't so huge that it can't be store in memory):

$ cat tst.awk
BEGIN { FS="|" }
{
    for ( i=1; i<=NF; i++ ) {
        wid = length($i) + 2
        wids[i] = (wid > wids[i] ? wid : wids[i])
        vals[NR,i] = $i
    }
}
END {
    line = "+"
    for ( i=1; i<=NF; i++ ) {
        line = line sprintf("%*s",wids[i],"") "+"
    }
    gsub(/ /,"-",line)
    print line

    for ( fnr=1; fnr<=NR; fnr++ ) {
        out = FS
        for ( i=1; i<=NF; i++ ) {
            out = out sprintf("%-*s",wids[i]," "vals[fnr,i]) FS
        }
        print out
        if ( fnr == 1 ) {
            print line
        }
    }

    print line
}

$ awk -f tst.awk file
+------+-------+---------------------+
| Type | Value | Adicional Resources |
+------+-------+---------------------+
| foo  | bar   | whatever            |
+------+-------+---------------------+
0
dawg On

Given:

$ cat file
Type,Value,Adicional Resources
boop,bop,boom
long arm,of the,long armed lawman

You can use mlr to pretty print a table:

$ mlr --icsv --opprint --barred cat file
+----------+--------+---------------------+
| Type     | Value  | Adicional Resources |
+----------+--------+---------------------+
| boop     | bop    | boom                |
| long arm | of the | long armed lawman   |
+----------+--------+---------------------+

If your input field separator is | vs , you can set that:

$ mlr --icsv --ifs '|' --opprint --barred cat file
# same output 

Or a Ruby:

ruby -r csv -e 'BEGIN{opts={:col_sep=>"|"} }
tbl=CSV.parse($<.read, **opts)
cw=tbl.transpose.map{|a| a.max_by{|e| e.length}}.map(&:length)
line="+#{cw.map{|w| "-"*(w+2) }.join("+")}+"
to=tbl.map{|row| row.zip(cw).map{|f,w| "|#{(" "+f).ljust(w+2)}" }.join()+"|" }
to.unshift(line); to.insert(2, line); to<<line
puts to.join("\n")
' file 
# same output

Or, pure awk:

awk -F"|" '
FNR==NR{for (i=1;i<=NF;i++) c[i]=length($i)>c[i] ? length($i) : c[i]; next}
{
    for (i=1;i<=NF; i++) 
        printf "| %-*s%s",c[i]+2,$i,(i<NF ? "" : " |\n")
}

' file file | awk '
FNR==1{
    line=$0
    gsub(/[^|]/,"-", line)
    gsub(/\|/,"+", line)
    printf "%s\n%s\n%s\n",line,$0,line
    next
}
{print}
END {print line}'
# same output

Any of these will take stdin vs reading a file if you are producing the output from a Bash loop.

ie:

<stdin> | mlr --icsv --ifs '|' --opprint --barred cat