user623990
user623990

Reputation:

Odd string parsing with double colons in Perl

I've stumbled upon something odd with Perl string parsing. Here's an example:

$word = "hello";
$test1 = "colon:values::$word:test";
$test2 = "colon:values::$word::test";

// test1 prints: "colon:values::hello:test"
// test2 prints: "colon:values::"

So if Perl sees a double colon after a variable in a string, it will (I assume) think you're trying to use a package name. I guess it's trying to load "Hello::test" and not finding anything - hence the early termination of the string.

Is this normal? I find it pretty counter-intuitive. I was able to work around it by escaping the first colon like so:

$works = "colon:values::$word\::test";

Is this a bug or am I completely missing something obvious?

Upvotes: 4

Views: 1134

Answers (3)

Oesor
Oesor

Reputation: 6642

While that works, a more widespread way to handle the case is to disambiguate the name of your variable with braces:

perl -E "my $word = 'red'; say qq|colon::values::${word}::test|"
colon::values::red::test

This is documented in perldata:

As in some shells, you can enclose the variable name in braces to disambiguate it from following alphanumerics (and underscores). You must also do this when interpolating a variable into a string to separate the variable name from a following double-colon or an apostrophe, since these would be otherwise treated as a package separator:

$who = "Larry";
print PASSWD "${who}::0:0:Superuser:/:/bin/perl\n";
print "We use ${who}speak when ${who}'s here.\n";

Without the braces, Perl would have looked for a $whospeak, a $who::0, and a $who's variable. The last two would be the $0 and the $s variables in the (presumably) non-existent package who.

Upvotes: 9

David W.
David W.

Reputation: 107080

Perl uses namespaces for variable names, and Perl when Perl sees :: or a ', it assumes what's before is a package (i.e. namespace) name. It is similar to this error:

use strict;
use warnings;    # You're using these? Aren't you?

my $foo = "bar"
print "The magic word is $foo_for_you\n";

Since the underscore is a valid character for variable names, Perl assumes that you want the variable $foo_for_you and not $foo with _for_you appended to the value. So, would you consider this a bug or a feature? Is it any difference from this:

print "The magic word is $foobar\n";   # Whoops! my variable is $foo.

The way to get around this is to make it absolutely clear that $foo is your variable:

print "The magic word is " . $foo . "_for_you\n";
printf "The magic word is %s_for_you\n", $foo;
print "The magic word is ${foo}_for_you\n";

The same issue if you had $foo::for::you (or $foo'for'you) In this case, Perl is looking for a variable called $you in the namespace foo::for. As you can imagine, you can use similar solutions:

print "The magic word is " . $foo . "::for::you\n";
printf "The magic word is %d::for::you\n", $foo;
print "The magic word is ${foo}::for::you\n";

Namespaces are used to help keep variables in Perl modules from modifying variables in your program. Imagine calling a function in a Perl package, and suddenly discovering that a variable you were using in your program was changed.

Take a look at File::Find and you can see variables with the package namespace attached to them ($File::Find::name and $File::Find::dir are two examples).

Upvotes: 1

Matthew Walton
Matthew Walton

Reputation: 9969

The reason this is happening is because it's trying to look up $word::test as a variable name. As you've correctly deduced, the package separator :: is part of the name, but it's not going to follow the value of $word because it's not being used as a symbolic reference here (that requires more syntax).

Oesor's solution will solve your problem - the general way to fix this kind of issue is to use ${identifier} syntax. Basically use it any time you're interpolating a variable into a string and you need to follow it with something that's a valid identifier character or related symbol (i.e. ::).

Upvotes: 3

Related Questions