7
#!/usr/bin/perl
my $var_a;
$sub_a = "a";
$var_a = "a";
print ${var_."$sub_a"},"\n";


$sub_b = "b";
$var_b = "b";
print ${var_."$sub_b"},"\n";

__DATA__

b

Why is b printed, but not a? This seems like very unexpected behaviour to me.

I'm trying to use a variable with a substituted name. In practice, I cannot just not declare the variable, since the assignment is being done in a forloop and thus has different lexical scope.

EricR
  • 1,487
  • 2
  • 21
  • 42
  • `Why does this happen?` Define "this"? Instead of asking about how to work "hacks", you should ask about your problem and how to do it properly. Using strings as references violates the `strict` pragma, and should be avoided. – TLP Aug 02 '12 at 18:54
  • 1
    @TLP, I'm aware. However, perl does support variables in variable names, and I wish to make use of that fact. I assumed the "this" would be obvious. "a" is not printed, while "b" is. I've adjusted the question. I have every intention of doing it properly with a hash, and had from the start. I wished to see if I could implement and discovered what I believed to be strange behaviour that I didn't understand. – EricR Aug 02 '12 at 19:19
  • I think I am too late to ask here but in continuation to same, I want to use something like this @{array_type_."$i"_."$s"}. @{array_type_."$i"} this would work fine but the other won't. – Ramneek Singh Kakkar Jun 20 '20 at 13:55

3 Answers3

10

Please note that this has NOTHING to do with the fact that you are using variables to contain the name of other variable.

The reason this doesn't work is because ${"var_a"} construct in reality refers to a package level variable $main::var_a.

Since $var_a is declared as a lexical variable, it's a DIFFERENT identifyer, and therefore ${"var_a"} is undef.

You can see that if you change my $var_a to our $var_a

our $var_a="a";
my $var_b="b";
$var_c="c";

print ${"var_a"},"\n";
print ${"var_b"},"\n";
print ${"var_c"},"\n";

######## RESULTS:
a

c

As others noted, while there is a good explanation for why what you are trying to do doesn't work, WHAT you are doing is likely the wrong approach. You should almost NEVER use this method unless there's no better way; without your problem it's not clear what the better way is but most likely would be a hash like TLP's answer says.

DVK
  • 126,886
  • 32
  • 213
  • 327
  • Thank you, that answered the technical question. I wasn't really looking for a solution to my "problem", so much as an explanation for the behaviour. – EricR Aug 02 '12 at 19:26
4

If you want a symbolic reference, you want to use a hash.

my %user_vars = (var_a => 'what ever');
my $sub_a = 'a';
print $user_vars{"var_$sub_a"};


Output: 'what ever'

Do not use symbolic references for this purpose because they are unnecessary and likely very harmful in the context of your question. For more information, see Why it's stupid to 'use a variable as a variable name'?. There is almost always a better way to do this, one way is to make sub_a and var_a arrays, where sub_a is the key for var_a. It is good practice to always lock your hashes when you are not using them to prevent injection attacks, for example:

use strict;
use warnings;
use Hash::Util qw(lock_hash unlock_hash);

my @sub_a;
my %var_a;
lock_hash(%var_a);

@sub_a = qw(a b c);          # the keys

unlock_hash(%var_a);
%var_a = (a => 'Value for a', b => 'Value for b', c => 'Value for c');
lock_hash(%var_a);

print $var_a{$sub_a[0]};     # 'Value for a'
print $var_a{'banana'};      # Fatal error when accessing data for banana.
daxim
  • 39,270
  • 4
  • 65
  • 132
Branden S. Smith
  • 1,161
  • 7
  • 13
  • Can you please cite locking of hashes to prevent injection attacks? Cheers. – EricR Aug 02 '12 at 19:26
  • An injection attack can be caused if the variable hash ever receives user input that is something that I was told in college, at my first job, and consequently it's what I tell others. Always lock hash arrays, and only unlock them after you preform a check on the variables you are planning on adding to them. I've added a link to a reference on how injection attacks can be caused. – Branden S. Smith Aug 03 '12 at 00:45
  • 1
    Sorry, key locking does not help with injection attacks. Rather, you should enable taint checks, see [perldoc perlsec](http://p3rl.org/sec). – daxim Aug 03 '12 at 00:52
  • Good to know, thanks daxim. I will look into implementing that in my next project. Is hash locking outdated? – Branden S. Smith Aug 03 '12 at 01:10
1

Using strings as references violates the strict pragma, which you should always use. You are better off using a hash, if you want to do it properly:

use strict;
use warnings;

my %hash;

$hash{"var_a"} = "a";
print $hash{"var_a"},"\n";
TLP
  • 66,756
  • 10
  • 92
  • 149