views:

281

answers:

6

How can I include a variable in a printf expression?

Here's my example:

printf "%${cols}s", $_;

Where $cols is the number of columns and $_ is a string.

The statement results in an "Invalid conversion" warning.

The problem ended up being that I forgot to chomp the variable. Gah. Thanks everyone.

+3  A: 

Your current method should work

perl -e'my $cols=500; $_="foo"; printf "%${cols}s\n\n", $_;'
Evan Carroll
You should test it with `-Mstrict` and `-Mwarnings`, but it does work. Curious why the OP is getting warnings now...
Chris Lutz
I did before I posted ;) I didn't want it to look like this answer took me more than 5 seconds. Testing this stuff is *really* simple with Perl, I really don't even have to cut that stuff out to drive my point home.
Evan Carroll
+1  A: 

The following seems to work for me:

#!/bin/perl5.8 -w
use strict;
my $cols = 5;
my $a = "3";
printf "%${cols}d\n", $a;

yields

28$ ./test.pl
    3

29$ 
kirillka
+1  A: 

I cannot reproduce your problem. The following code works fine:

use strict;
use warnings;

my $cols=40;
while (<>) {
    printf "%${cols}s\n", $_;
}

It prints any input line using at least 40 columns of width.

Danilo Piazzalunga
+10  A: 

Your interpolated variable $cols looks like its supposed to be a number, say 10, so

"%${cols}s"

should interpolate and be equivalent to

"%10s"

which is a valid format string.

If however $cols was something other than a number or valid format string, you'd get the warning.

For example, if:

$cols = "w";

that would result in "%ws" as a format string - giving the error you quote:

Invalid conversion in printf: "%w"

Valid format information can be found here.

martin clayton
+1 This is relevant, as it correctly shows the error message. I think I got what the actual problem was, but it's impossible to know without hearing from the OP.
Chris Lutz
+7  A: 

I figured out your specific problem. Your code is correct. However, I suppose $cols might be a number read from user input, say like this:

my $cols = <STDIN>;

This works, and in numeric context $cols will appear to be a number, but the problem is that $cols isn't appearing in numeric context here. It's in string context, which means that instead of expanding to "%5s", your format string expands to "%5\ns". The newline there is mucking up the format string.

Change the code where you read $cols to this:

chomp(my $cols = <STDIN>);

See the documentation on chomp, as you may want to use it for other input reading as well.

Chris Lutz
This was the answer. I had a new line appended to the end of the user input where the user had input the number of columns. MANY THANKS! :)
Louise
+5  A: 

Always use * in your format specifier to unambiguously indicate variable width! This is similar to the advice to use printf "%s", $str rather than printf $str.

From the perlfunc documentation on sprintf:

(minimum) width

Arguments are usually formatted to be only as wide as required to display the given value. You can override the width by putting a number here, or get the width from the next argument (with *) or from a specified argument (with e.g. *2$):

printf '<%s>', "a";       # prints "<a>"
printf '<%6s>', "a";      # prints "<     a>"
printf '<%*s>', 6, "a";   # prints "<     a>"
printf '<%*2$s>', "a", 6; # prints "<     a>"
printf '<%2s>', "long";   # prints "<long>" (does not truncate)

If a field width obtained through * is negative, it has the same effect as the - flag: left-justification.

For example:

#! /usr/bin/perl

use warnings;
use strict;

my $cols = 10;
$_ = "foo!";
printf "%*s\n", $cols, $_;
print "0123456789\n";

Output:

      foo!
0123456789

With the warnings pragma enabled, you'll see warnings for non-numeric width arguments.

Greg Bacon