I have a data file with five columns, when I use the printf command in awk, the output isn't aligned.
118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 I need to use all columns with the same space separator, independent is the number are tens, hundreds, or thousands like this:
118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 How can I do this using printf in awk?
I'm using this:
awk '{printf "%d %s %d %s %d %s %d %s %d\n", $1,"",$2,"",$3,"",$4,"",$5}' test 45 Answers
Use column on the output rather than trying to format it with awk:
$ column -t -R'1,2,3,4,5' file 118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 Your version of column may already support -R0 which means "right align all columns" so you don't need to list them, see .
As @glennjackman pointed out in comments:
2The BSD-derived
columnon MacOS does not have the-Roption. Have to dorev file | column -t | revon a mac
Most printf formats allow the width to be supplied as an argument to a * format specifier, eg:
printf "%*s", 5 "abc" Is evaulated as:
printf "%5s, "abc" One awk idea making use of this printf/* feature:
awk ' FNR==NR { for (i=1;i<=NF;i++) w[i]= length($i) > w[i] ? length($i) : w[i] # find max width for each column next } { pfx="" for (i=1;i<=NF;i++) { printf "%s%*s", pfx, w[i], $i pfx=" " # (aligned) column delimiter == 2 spaces for columns 2 to NF } print "" # terminate current line } ' five.dat five.dat NOTES:
- requires 2 passes of the input file (could be rewritten to use a single pass but will need to store the entire file in memory)
- assumes the minimum delimiter between (aligned) columns is 2 spaces
This generates:
118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 The 3 easiest solution are: 1) pipe the output to column -t, 2) use a tab separator (doesn't completely align the text, but for the sample input is sufficient, and 3) print each column on a fixed width.
$ cat input 118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 $ awk '($1=$1) || 1' OFS=\\t input 118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 $ awk '{printf "%5s%5s%5s%5s%5s\n", $1, $2, $3, $4, $5}' input 118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 Here's one that requires two passes of the data (hence the file file in the end):
$ awk 'NR==FNR { # first pass for(i=1;i<=NF;i++) if(m[i]=="" || m[i]<length($i)) # get max field widths m[i]=length($i) next } { # second pass for(i=1;i<=NF;i++) printf "%" m[i] "s%s",$i,(i==NF?ORS:" ") # output two spaces in between }' file file # two passes, twice the file Output:
118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 Here is an awk to do that:
awk 'FNR==NR{for(i=1;i<=NF;i++) if (w[i]<length($i)) w[i]=length($i); next} {for(i=1;i<=NF;i++) printf("%*s%s", w[i], $i, i<NF ? OFS : ORS)} ' file file Prints:
118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0 Then if you want to field size to increase, just add that:
awk -v wp=5 'FNR==NR{for(i=1;i<=NF;i++) if (w[i]<length($i)+wp) w[i]=length($i)+wp; next} {for(i=1;i<=NF;i++) printf("%*s%s", w[i], $i, i<NF ? OFS : ORS)} ' file file 118 96 105 106 0 119 97 106 107 0 120 98 107 108 0 121 99 108 109 0 122 100 109 110 0 123 101 110 111 0 124 102 111 23 0 125 11 12 112 0 126 103 112 113 0 127 104 113 114 0 128 105 114 115 0