Printf in awk with the same space between columns

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 
4

5 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:

The BSD-derived column on MacOS does not have the -R option. Have to do rev file | column -t | rev on a mac

2

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 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like