Of course, there are plenty of potential pitfalls to this simple approach
Here's another approach to that without invocation of a sprintf binary, so no shell danger. Simply interpolate the "babycart operator" @{[]} into the format for any %'d conversion, call sprintf, then eval the result as a string. So something like
cprintf "There are at least %'d ways to do it!\n", 42e6;
will effectively result in
printf "There are at least @{[commify('%d')]} ways to do it!\n", 42e6;
with the difference that, unlike printf, the interpolation is done before the expansion of @{[]}
(printf evaluates the expansion first, i.e. it calls commify with a literal '%d', and then interpolates the value. Bug?)
but without the @{[]} evaluation being done at the time the format is assembled.
All other perl sprintf conversions and flags can be used within the format.
This does the trick:
sub commify {
local $_ = shift;
my $spc = ''; s/^(\s+)// and $spc = $1; # trim and save leading sp
+ace
my $adj = 0;
$adj++ while s/^([-+]?\d+)(\d{3})/$1,$2/;
$spc =~ s/.{$adj}//; # adjust space for commas added
s/\s{0,$adj}$// if /\s$/; # adjust right padding
return $spc . $_;
}
sub cprintf {
(my $format = shift) =~
s{
\%(['+0-9.-]+)?([df]) # capture all valid %d and %f flags
+and modifiers
}{
my $p = $1;
my $c = $2;
$p =~ s/'// ? "\@{[commify('%$p$c')]}" : "%$p$c"
}gex;
my $str = sprintf $format, @_;
print eval "\"$str\"";
}
cprintf "%+'012d\n", 1e6;
cprintf "<%-'12.6d>\n", 1e6;
cprintf "<%-+12.6'd>\n", 1e6;
cprintf "<%+12.6'd>\n", -1e6;
cprintf "<%+12.6d>\n", -1e6;
cprintf "<%+12.2'f>\n", 1234.5;
cprintf "There are at least %'d ways to do it!\n", 42e6;
__END__
+00,001,000,000
<1,000,000 >
<+1,000,000 >
< +1,000,000>
< -1,000,000>
< -1000000>
< +1,234.50>
There are at least 42,000,000 ways to do it!
Of course, neither your nor my approach works for the printf FILEHANDLE FORMAT, LIST form of printf.
update: fixed format substitution
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'