1

I want to print an 'echo' command output which has fields that is separated by '|' into columns with a fixed same width for all column. Number of columns may vary hence, the width should be a global setting applicable for all columns.

Sample input

1234|sdan:active:running|sdax:active:running|sdbh:active:running|sdcv:active:running|sddf:active:running|sddp:active:running|Total paths 6 : OK
1235|sdc:active:running|sdm:active:running|sdw:active:running|sdbk:active:running|sdbu:active:running|sdce:active:running|Total paths 6 : OK
1236|sdam:active:running|sdaw:active:running|sdbg:active:running|sdde:active:running|sdcu:active:running|sddo:active:running|Total paths 6 : OK
tso
  • 4,732
  • 2
  • 22
  • 32
Vijesh Kk
  • 141
  • 2
  • 11

5 Answers5

2

Do you need awk?

cat input.txt | tr '|' ' ' | rev | column -t | rev

You can replace tr with awk if you really want to, but the tabulating magic happens with rev and column -t.

Credit

drootang
  • 2,351
  • 1
  • 20
  • 30
  • What's the purpose of `rev` here? I don't see any difference when they are omitted. – glenn jackman Aug 19 '17 at 12:26
  • diito for `cat` and the first pipe vs `< `. Also, `column` just creates columns wide enough to hold each field, it doesn't make all columns the same width as each other as the OP asked for. – Ed Morton Aug 19 '17 at 12:47
  • 1
    `column -t` just cleaned up a mess I was struggling with. I'm so glad you answered this question and didn't delete your answer despite the down vote. – Christopher Bottoms Mar 06 '20 at 20:25
2

Given:

$ cat file
1234|sdan:active|sdax:active|sdbh:active|sdcv:active|sddf:active|sddp:active|Total paths 6 : OK
1235|sdc:active|sdm:active|sdw:active|sdbk:active|sdbu:active|sdce:active|Total paths 6 : OK
1236|sdam:active|sdaw:active|sdbg:active|sdde:active|sdcu:active|sddo:active|Total paths 6 : OK

Here is an awk that will print all fields the longer of the value of w or the length of the string in that field:

$ awk 'BEGIN{OFS=FS="|"; w=8}
 {for (i=1;i<=NF;i++) printf "%-*s%s", w, $i, i==NF ? ORS : OFS}' file
1234    |sdan:active|sdax:active|sdbh:active|sdcv:active|sddf:active|sddp:active|Total paths 6 : OK
1235    |sdc:active|sdm:active|sdw:active|sdbk:active|sdbu:active|sdce:active|Total paths 6 : OK
1236    |sdam:active|sdaw:active|sdbg:active|sdde:active|sdcu:active|sddo:active|Total paths 6 : OK

If you want to cut the longer fields to fit that fixed width:

$ awk 'BEGIN{OFS=FS="|"; w=11}
     {for (i=1;i<=NF;i++) printf "%-*s%s", w, substr($i,1,w), i==NF ? ORS : OFS}
     ' file
1234       |sdan:active|sdax:active|sdbh:active|sdcv:active|sddf:active|sddp:active|Total paths
1235       |sdc:active |sdm:active |sdw:active |sdbk:active|sdbu:active|sdce:active|Total paths
1236       |sdam:active|sdaw:active|sdbg:active|sdde:active|sdcu:active|sddo:active|Total paths

If you want to run through the file to get the width that will fit all fields then use that to print all fields in that fixed width:

$ awk 'BEGIN{OFS=FS="|"}
       NR==FNR {for (i=1;i<=NF;i++) w=length($i)+1>w ? length($i)+1 : w; next}
               {for (i=1;i<=NF;i++) printf "%-*s%s", w, $i, i==NF ? ORS : OFS}
     ' file file
1234               |sdan:active        |sdax:active        |sdbh:active        |sdcv:active        |sddf:active        |sddp:active        |Total paths 6 : OK 
1235               |sdc:active         |sdm:active         |sdw:active         |sdbk:active        |sdbu:active        |sdce:active        |Total paths 6 : OK 
1236               |sdam:active        |sdaw:active        |sdbg:active        |sdde:active        |sdcu:active        |sddo:active        |Total paths 6 : OK 
dawg
  • 98,345
  • 23
  • 131
  • 206
1

You can use just column like shown below:

$ column -s '|' -o '|' -t input.txt 
1234|sdan:active:running|sdax:active:running|sdbh:active:running|sdcv:active:running|sddf:active:running|sddp:active:running|Total paths 6 : OK
1235|sdc:active:running |sdm:active:running |sdw:active:running |sdbk:active:running|sdbu:active:running|sdce:active:running|Total paths 6 : OK
1236|sdam:active:running|sdaw:active:running|sdbg:active:running|sdde:active:running|sdcu:active:running|sddo:active:running|Total paths 6 : OK
Guilherme Amadio
  • 372
  • 2
  • 13
0

This is easy enough to do simply by modifying each of the fields on every line. The following transcripts shows how to do this:

pax> cat qq.awk
BEGIN { OFS = FS = "|" }
{   for (i = 1; i <= NF; i++) {
        $i = sprintf("%-12s", $i)
    }
    print
}

pax> cat qq.input
1234|sdan:active|sdcv:running|sddf:dead|sddp:active|3 paths:BAD
1235|sdc:active|sdm:active|sdw:running|sdbk:running|4 paths:OK
1236|sda:active|sdm:dead|2 paths:BAD

pax> awk -f qq.awk qq.input
1234        |sdan:active |sdcv:running|sddf:dead   |sddp:active |3 paths:BAD
1235        |sdc:active  |sdm:active  |sdw:running |sdbk:running|4 paths:OK
1236        |sda:active  |sdm:dead    |2 paths:BAD

The important thing there is the sprint within the for loop (said loop does every single field):

$i = sprintf("%-12s", $i);

This replaces each field with the content of that field space-padded on the right out to (at least) that specified width.

If you want a one-liner rather than the nicely formatted script, you can use:

awk 'BEGIN{OFS=FS="|"}{for(i=1;i<=NF;i++){$i=sprintf("%-12s",$i)};print}' qq.input
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • ITYM `for (i = 1; i <= NF; i++)`. As written the full record ($0) will have the sprintf applied to it (which will do nothing) and the final field ($NF) will not be formatted to the same width as the others. Also, the trailing semi-colons are usless. Awk is not C so it's array, string and field indices start at 1, not 0, and it does not need semi-colons to terminate statements. just newlines. – Ed Morton Aug 19 '17 at 12:55
  • @Ed, good point, I'll fix that. The semicolons I tend to use since I frequently format the code in answers such as these to be readable but then put it all on one line for actually *doing* the work. – paxdiablo Aug 19 '17 at 14:52
0

awk is easier, but it can be done with sed:

width=12
printf -v blanks '%*s' "$width"
sed -rf <(echo "s/\|/${blanks}|/g; s/(.{${width}})[^|]*\|/\1|/g") input.txt
Walter A
  • 19,067
  • 2
  • 23
  • 43