Ju-Yeol Jung Ju-Yeol Jung - 30 days ago 8
Perl Question

Variable was returned to previous value in Perl

I'm just one-month experience in perl.
For perl-based program executing problem, the commend variable was returned to previous value.
What is the problem?
Here is the code.

1st();
2nd();
sub 1st {
$cmd = "cat asdf";
}
sub 2nd {
if ( $code =~ /aa/ ) {
my $cmd = "$reg $annotation";
out_log($cmd);
} else {
my $cmd = "$reg $annotation";
out_log($cmd);
}
out_log("$cmd");
open (Work,$cmd);
}


In this state, the $cmd was registered in if statement, but executing $cmd after if statement, the $cmd value was returned subroutine 1st's value.
Thanks for your advices.

Answer Source

You are mixing lexical and package variables. If your program had use strict and use warnings, this would be quite obvious.

If you do not declare a variable with my, Perl will assume it's a package variable. It will be visible from every part of your program (in the same namespace).

If you declare the variable with my, it will be lexical. That means that it only exists inside the scope it was created in.

my $foo = 1;         #
                     #
if ($foo) {       #  #
    my $bar = 2;  #  #
}                 #  # 
                  ^  ^
                  |  | scope that $foo exists in
                  | scope that $bar exists in

The same thing is happening here.

You are setting the package variable $::cmd (with :: being the main namespace) to "cat asdf" inside the 1st sub. You then call the 2nd sub, which will go into the else branch. In that scope, it will create a new lexical $cmd. It's only valid in that part of the program. It is then passed to out_log(), which probably prints it. Afterwards, you pass $::cmd with the "cat asdf" value to out_log(). At that point the new $cmd does not exist any more.

code with freehand lines

If you had use strict in your program, the program would not work at all, because the default package variable behavior is turned off in that case, so you have to define variables.

In fact you should not operate with package variables at all, but instead pass arguments to your functions.

In addition to that, there are a few other things that are not good practice in your program. You should use 3-argument open and a lexical filehandle, and also check the return value of open.

Names of functions cannot start with numbers, so 1st and 2nd are not valid names. It's better to name things after what they do or represent. That makes it easier to read your program later.

A full program might look like that.

use strict;
use warnings;

my ($code, $reg, $annotation); # these values come from somewhere...

run_cmd( compose_cmd(), $code, $reg, $annotation );

sub compose_cmd {
    return "cat asdf";
}

sub run_cmd {
    my ( $cmd, $code, $reg, $annotation ) = @_;

    if ( $code =~ /aa/ ) {
        my $cmd = "$reg $annotation";
        out_log($cmd);
    }
    else {
        my $cmd = "$reg $annotation";
        out_log($cmd);
    }
    out_log("$cmd");
    open my $fh, '<', $cmd or die $!;

    # do stuff with $fh ...
}

sub out_log {
    print @_;
}