Perl: how to make variables from requiring script

2020-03-25 06:21发布

问题:

example

out.pl:

(my|our|local|global|whatever???) var = "test";
require("inside.pm");

inside.pm:

print $var;

I don't want to use packages - it's overwhelming my needs :) thanks!

回答1:

It will work with our.

$ cat out.pl
our $var = "test";
require("inside.pm");

$ cat inside.pm 
print "Testing...\n";
print "$var\n";

$ perl out.pl
Testing...
test

This works because our makes $var global, and inside.pm is being executed in the scope with $var defined. Not sure it is recommended technique, but it is an interesting question nevertheless!

EDIT: Need to clarify (okay patch) the answer based on a comment:

From the documentation on the Perl function our:

our associates a simple name with a package (read: global) variable in the current package, for use within the current lexical scope. In other words, our has the same scoping rules as my or state, but does not necessarily create a variable.

So using our, we get $var with the current package (here probably main) and we can use it in its scope. In effect it is then "global" to the code in the file you are requiring-in.

A true global is introduced without the our, because variables default to global. But I don't know anyone that would recommend them.



回答2:

You are always using a package, even if you don't use the package declaration. By default, you're working in package main.

All variables you declare with our are package variables and should be available package wide. Here's an example:

#! /usr/bin/env perl
# test2.pl

use strict;
use warnings;

our $foo = "bar";
1;

Since $foo is declared as a package variable, it will be available in other programs:

#! /usr/bin/env perl
use strict;
use warnings;

require "test2.pl";

our $foo;
print "The value of \$foo is $foo\n";

Now I've given you enough rope, I'm going to tell you not to hang yourself with it.

This is a REALLY, REALLY BAD IDEA. Notice that $foo gets a value from some sort of mysterious mechanism that's almost impossible to figure out?

Packages are too complex? Really? It's not that hard! Look at this example:

#! /usr/bin/env perl
# test2.pm

package test2;
use strict;
use warnings;

our $foo = "bar";
1;

Not much different than before except I added the package declaration and now call my program test2.pm instead of test2.pl.

Here's how I access it:

#! /usr/bin/env perl
use strict;
use warnings;

use test2;

print "The value of \$foo from package test2 is $test2::foo\n";

All I had to do was use the package name in the variable. This is a BAD IDEA, but it's way better than the REALLY, REALLY BAD IDEA shown above.

At least, you know where the value came from. It came from test2.pm. And, you could access the variable if you set it in a subroutine.

#! /usr/bin/env perl
# test2.pm

package test2;
use strict;
use warnings;

sub fooloader {
    our $foo = "bar";
}
1;

Notice that $foo is set in the subroutine fooloader. And, here's my other program to access it:

#! /usr/bin/env perl
use strict;
use warnings;

use test2;

&test2::fooloader();
print "The value of \$foo from package test2 is $test2::foo\n";

Now, you could use the Exporter to export your subroutines (and even variables), but that's not something you see too much anymore. Mainly because it is a REALLY BAD IDEA. Not as bad as the original REALLY REALLY BAD IDEA, but worse than the BAD IDEA above:

#! /usr/bin/env perl
# test2.pm

package test2;
use base qw(Exporter);

our @EXPORT = qw(fooloader);
use strict;
use warnings;

sub fooloader {
    our $foo = "bar";
}
1;

Now, I can use subroutine fooloader without the package name:

#! /usr/bin/env perl
use strict;
use warnings;

use test2;

fooloader();
print "The value of \$foo from package test2 is $test2::foo\n";

The problem, of course, is that you have no real idea where the subroutine fooloader is coming from. If you used @EXPORT_OK instead of @EXPORT, you could have then use use test2 qw(fooloader); and document where the fooloader function was coming from. It'll also help you to know not to create your own fooloader function in your own program and override the one you imported. Then, wonder why your program no longer works.

By the way, you could also export variables and not just functions. However, that becomes a REALLY, REALLY, REALLY BAD -- NO TERRIBLE IDEA because it violates every reason why you use packages in the first place. If you're going to do that, why bother with packages? Why not simple take a gun and shoot yourself in the foot?

The best and preferred way is to use object oriented Perl and do it in the THOROUGHLY CORRECT WAY. A way where you know exactly what's going on and why. And, makes it easy to figure out what your code is doing. A way that keeps errors at a minimum.

Behold the thoroughly object oriented Test2 class:

#! /usr/bin/env perl
# Test2.pm

package Test2;

sub new {
    my $class = shift;

    my $self = {};
    bless $self, $class;

    return $self;
}

sub FooValue {
    return "bar";
}
1;

And using that Test2 class:

#! /usr/bin/env perl
use strict;
use warnings;

use Test2;

my $tester = Test2->new;

print "The value of foo from package test2 is " . $tester->FooValue . "\n";

The reason this is the best way to do it is because even if you use packages, you could manipulate the value of $test2::foo and it will be changed in your entire program. Imagine if this was say $constants::pi and somewhere you changed it from 3.14159 to 3. From then on, using $constants::pi would give you the wrong value. If you use the object oriented method, you couldn't change the value of the method Constant->Pi. It will always be 3.14159.

So, what did we learn today?

We learned that it is very easy in Perl to do something that's a REALLY, REALLY BAD IDEA, but it doesn't take all that much work to use packages, so it merely becomes a BAD IDEA. And, if you start learning a bit of object oriented Perl, you can actually, without too much effort, do it all in the THOROUGHLY CORRECT WAY.

The choice is yours to make. Just remember the foot you're shooting will probably be your own.