11

Suppose I have a file like this:

$ cat a
hello this is a sentence
and this is another one

And I want to print the first two columns with some padding in between them. As this padding may change, I can for example use 7:

$ awk '{printf "%7-s%s\n", $1, $2}' a
hello  this
and    this

Or 17:

$ awk '{printf "%17-s%s\n", $1, $2}' a
hello            this
and              this

Or 25, or... you see the point: the number may vary.

Then a question popped: is it possible to assign a variable to this N, instead of hardcoding the integer in the %N-s format?

I tried these things without success:

$ awk '{n=7; printf "%{n}-s%s\n", $1, $2}' a
%{n}-shello
%{n}-sand

$ awk '{n=7; printf "%n-s%s\n", $1, $2}' a
%n-shello
%n-sand

Ideally I would like to know if it is possible to do this. If it is not, what would be the best workaround?

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
fedorqui
  • 275,237
  • 103
  • 548
  • 598

4 Answers4

24

If you use * in your format string, it gets a number from the arguments

awk '{printf "%*-s%s\n", 17, $1, $2}' file
hello            this
and              this

awk '{printf "%*-s%s\n", 7, $1, $2}' file
hello  this
and    this

As read in The GNU Awk User’s Guide #5.5.3 Modifiers for printf Formats:

The C library printf’s dynamic width and prec capability (for example, "%*.*s") is supported. Instead of supplying explicit width and/or prec values in the format string, they are passed in the argument list. For example:

w = 5
p = 3
s = "abcdefg"
printf "%*.*s\n", w, p, s

is exactly equivalent to:

s = "abcdefg"
printf "%5.3s\n", s
fedorqui
  • 275,237
  • 103
  • 548
  • 598
jaypal singh
  • 74,723
  • 23
  • 102
  • 147
  • 1
    Splendid, yes! Do you have any source explaining this behaviour? – fedorqui Aug 20 '14 at 15:03
  • 2
    don't know this. +1 dude – Kent Aug 20 '14 at 15:05
  • 1
    @fedorqui Sure, it is stated [here](http://www.gnu.org/software/gawk/manual/gawk.html#Format-Modifiers) – jaypal singh Aug 20 '14 at 15:06
  • 1
    These and the positional specifiers are my favorite underused (and under-supported) specifiers. – Etan Reisner Aug 20 '14 at 15:35
  • 1
    This has what I really like of answers: discovers a functionality that I did not know about and opens new fields and opportunities. Thanks a lot @jaypal! – fedorqui Aug 20 '14 at 15:43
  • 2
    All I could do was sit back and click the +1 ... :-). Y'know what functionality doesn't exist that I wish did though - to be able to specify any lead character when padding. Just like you can do `awk 'BEGIN{printf "%05d\n",17}'` to left-pad with zeros, it'd often be convenient if you could do `awk 'BEGIN{printf "%#5d\n",17}'` to pad with `#`s or any other character. That way if you wanted a string of 5 `#`s you could simply do `awk 'BEGIN{printf "%#5d\n",""}'` instead of `awk 'BEGIN{print gensub(/0/,"#","g",sprintf("%05d",""))}'` or similar. Oh well there we'd go towards language bloat again.. – Ed Morton Aug 20 '14 at 17:22
5

does this count?

idea is building the "dynamic" fmt, used for printf.

kent$   awk '{n=7;fmt="%"n"-s%s\n"; printf fmt, $1, $2}' f 
hello  this
and    this
Kent
  • 189,393
  • 32
  • 233
  • 301
  • Good idea! Quite similar to konsolebox, only that you use "model-view-controller" pattern :) – fedorqui Aug 20 '14 at 15:38
  • My preferred solution out the answers. Example: `git log --pretty=%ae | awk '{ n=length($1) ; if (n>l) l=n ; arr[tolower($1)]++ } END { fmt="%-"l"s: %4i\n" ; for (i in arr) printf(fmt, i, arr[i]) | "sort" }'` - Meaning, calculate the max length of the git author to use in the END block printf. – sastorsl Nov 25 '20 at 15:11
4

Using simple string concatenation.

Here "%", n and "-s%s\n" concatenates as a single string for the format. Based on the example below, the format string produced is %7-s%s\n.

awk -v n=7 '{ printf "%" n "-s%s\n", $1, $2}' file
awk '{ n = 7; printf "%" n "-s%s\n", $1, $2}' file

Output:

hello  this
and    this
konsolebox
  • 72,135
  • 12
  • 99
  • 105
  • 1
    Nice! This seems to be making it. Would you mind adding some explanation for future readers? – fedorqui Aug 20 '14 at 14:57
  • 1
    Many thanks, I would have selected this answer if jaypal's answer did not appear. Clear and well explained, thanks! – fedorqui Aug 20 '14 at 15:39
-1

you can use eval (maybe not the most beautiful with all the escape characters, but it works)

i=15
eval "awk '{printf \"%$i-s%s\\n\", \$1, \$2}' a"

output:

hello          this
and            this
Chris Maes
  • 35,025
  • 12
  • 111
  • 136