12

Using printf, one can print a character multiple times:

$ printf "%0.s-" {1..5}
-----

In awk I know that I can do something like:

$ awk 'BEGIN {while (i++ < 5) printf "-"}'
-----

But I wonder if awk's printf allows this as well.

I went through the printf modifiers page but could not find how. All in all, what the printf from Bash does is to expand {1..5} and print a - for every parameter it gets, so it is equivalent to saying

$ printf "%0.s-" hello how are you 42
-----

However, I lack the knowledge on how to mimic this behaviour with awk's printf, if it is possible, because this fails:

$ awk 'BEGIN {printf "%0.s-", 1 2 3 4 5}'
-
Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598

4 Answers4

13

I do not believe this is possible with awk's printf, as there is also no way to do this just with printf in C and C++.

With awk, I think the most reasonable option is using a loop like you have. If for some reason performance is vital and awk is creating a bottleneck, the following will speed things up:

awk 'BEGIN {s=sprintf("%5s","");gsub(/ /,"-",s);print s}'

This command will run logarithmically faster[1] Though, it won't cause a noticeable difference in performance unless you're planning on printing a character many times. (Printing a character 1,000,000 times will be about 13x faster.)

Also, if you want a one-liner and are using gawk, even though it's the slowest of the bunch:

gawk 'BEGIN {print gensub(/ /,"-","g",sprintf("%5s",""));}'

 

[1] While the sprintf/gsub command should always be faster than using a loop, I'm not sure if all versions of awk will behave the same as mine. I also do not understand why the while-loop awk command would have a time complexity of O(n*log(n)), but it does on my system.

Community
  • 1
  • 1
Jeffrey Cash
  • 1,023
  • 6
  • 12
  • 3
    Brilliant! Could you share the insight on how you got to know the time complexity? I timed `awk 'BEGIN {while (i++ < 1000000) printf "-"}'` and `awk 'BEGIN {s=sprintf("%1000000s","");gsub(/ /,"-",s);print s}'` and the difference was 0.102s vs 0.115s. – fedorqui May 19 '16 at 05:57
  • 1
    Absolutely. I wrote a bash function to benchmark four different awk commands: the while-loop one, the `gsub` one, the `gensub` one, and a control that only printed a single dash. This function tested all of the commands against a wide variety of lengths of inputs and repetitions, subtracting the dummy time from the other three. From this, I found that the gensub one was much slower than the rest, so I focused just on while vs gsub. I then took my data and played around with it with wolframalpha, custom math functions and a google sheet, leading me to those big-O values. – Jeffrey Cash May 19 '16 at 09:55
  • [Here is one of the google sheets](https://docs.google.com/spreadsheets/d/1P8neMgoyJf34uoV1JtQ3hiEZuL0LWnl55z0zTXjwJ-E/edit?usp=sharing), in case you're curious. It's the only part of the process I still have left – Jeffrey Cash May 19 '16 at 10:01
9

I know this is old but the width modifier can be used e.g.

l = some_value

print gensub(/ /, "-", "g", sprintf("%*s", l, ""))

will print a variable number of - depending on the value of l

This was GNU Awk 3.1.8

anubhava
  • 761,203
  • 64
  • 569
  • 643
Maurice1408
  • 319
  • 2
  • 7
3

If you can assume a (modest) upper bound on how long the result should be, how about something like this:

l = 5;
print substr("---------------------", 1, l);

Besides being dead simple, this has the benefit that it works in versions of AWK that lack the "gensub()" function.

user98761
  • 475
  • 3
  • 8
0

i know this post is old, but thought it would be worth demonstrating functionality to allow dynamic control of character string length using basic awk.

A simple example incorporating a string of length 'var' into a printf statement

$ echo "1 foo baa\n2 baa foo" | awk -v var=6 'BEGIN{for(i=1;i<=var;i++) l=l "-" }{printf "%s" l "%s" l "%s\n",$1,$2,$3}' 
1------foo------baa
2------baa------foo

You can either split the format string and insert your character string as I've done above. Or you can give the string it's own format specifier

$ echo "1 foo baa\n2 baa foo" | awk -v var=6 'BEGIN{for(i=1;i<=var;i++) l=l "-" }{printf "%s%s%s%s%s\n",$1,l,$2,l,$3}' 

both output the same.

A more complicated example that column justifies text. (I've actually used basic 'print' rather than 'printf' in this case, although you could use the latter, as above).

$ echo "Hi, I know this\npost is old, but thought it would be worth demonsrating\nfunctionality to allow a dynamic control of\ncharacter string lengths using basic awk." |
 
awk '{
   line[NR]=$0 ; 
   w_length[NR]=length($0)-gsub(" "," ",$0) ;  
   max=max>length($0)?max:length($0) ;  
   w_count[NR]=NF 
}END{
   for(i=1;i<=NR;i++) 
     {
     string="" ; 
     for (j=1;j<=int((max-w_length[i])/(w_count[i]-1));j++) 
        string=string "-" ; 
     gsub(" ",string,line[i]) ; 
     print line[i]
     }
}'

Hi,--------------I--------------know--------------this
post-is-old,-but-thought-it-would-be-worth-demonsrating
functionality---to---allow---a---dynamic---control---of
character---string---lengths---using---basic---awk.