Passing arguments to a perl package while using it
use My::Module LIST
does two things: 1) It require
s My::Module
; and 2) Invokes My::Module->import(LIST)
.
Therefore, you can write your module's import
routine to treat the list of arguments passed any which way you want. This becomes even easier if you are indeed writing an object oriented module that does not export anything to the caller's namespace.
Here's a rather pointless example:
package Ex;
use strict;
use warnings;
{
my $hello = 'Hello';
sub import {
my $self = shift;
my $lang = shift || 'English';
if ($lang eq 'Turkish') {
$hello = 'Merhaba';
}
else {
$hello = 'Hello';
}
return;
}
sub say_hello {
my $self = shift;
my $name = shift;
print "$hello $name!\n";
return;
}
}
__PACKAGE__;
__END__
And a script to use it:
#!/usr/bin/env perl
use strict;
use warnings;
use Ex 'Turkish';
Ex->say_hello('Perl');
Ex->import;
Ex->say_hello('Perl');
Output:
$ ./imp.pl
Merhaba Perl!
Hello Perl!
Perl using a variable to reference a module messes up passing parameters
Short: The observed behavior comes from use of ->
on a package name.
The arrow operator is used with a reference or with an object, which itself is a reference to a data structure that has been bless
-ed into its class. (Or with a class name, see below.) That object or the class name is quietly passed as the first argument so that the whole system would work. Note that the package in the question does not define a class (objects cannot be created with it).
From Arrow operator in perlop
"-> " is an infix dereference operator, just as it is in C and C++. If the right side is either a [...] , {...} , or a (...) subscript, then the left side must be either a hard or symbolic reference to an array, a hash, or a subroutine respectively. (Or technically speaking, a location capable of holding a hard reference, if it's an array or hash reference being used for assignment.) See perlreftut and perlref.
It continues, to statements of direct interest in this problem
Otherwise, the right side is a method name or a simple scalar variable containing either the method name or a subroutine reference, and the left side must be either an object (a blessed reference) or a class name (that is, a package name). See perlobj.
So in uses related to classes the left-hand side may contain the class name, and class methods can then be invoked on it (or it can be just queried). Given that a class is a package then this is a package name.
The situation in the question falls within this so the package name is passed to the subroutine. However, according to the above quote it seems that the sub can only be a method, which isn't the case here. So it may be that this use of ->
should really be disallowed. Either way, using it on a package which isn't a class strikes me as mistaken.
Update to clarification. This use was intended, to resolve an ambiguity in which package was loaded. The package name is saved into a variable and then the sub invoked on it, using the arrow operator. In this case code would have to be added to the sub to handle the first argument (package name) which is passed regardless of the invocation, by the courtesy of the arrow operator. But then we would have to allow a case when this is invoked on an object, ending up with a code that covers two distinct uses. I believe that it is better to change to a design that does not involve all this.
If you want to use a package, say as a library
File TOTO.pm
pacakge TOTO;
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = ('Exporter');
@EXPORT_OK = qw(prn); # This can be asked for by user of package
use Data::Dumper;
sub prn {
print Dumper(@_);
}
1; # important for 'require' when this is used
I've changed the sub name to prn
so that it's not a Perl library function. The main script
use warnings;
use strict;
use TOTO qw(prn);
prn("Hello World");
The fully qualified name TOTO::prn()
can always be used. If you wanted to make this a class that would require a bit more in the package.
This package, TOTO
, does not export anything by default, unless asked for. That's what @EXPORT_OK
sets up and that's why we need to list functions to import into main::
when use TOTO
. Start, for example, with perlmod
Import perl variables into the module
You can do that by referring to $main::db
in other packages. The main
namespace always point to globals in the primary namespace if there is none other given. You should read up on package
.
Note that this is not a very good idea as your modules will be dependent on main
having the connection. Instead, you should construct your objects in a way that let you pass a database handle in. If you require a db connection at all cost, either let them throw an exception or create their own db handle.
If you are not using OO code, make the database handle an argument of every function call.
Also note that it's best practice to name the database handle $dbh
.
Let's look at this for non-OO (Foo
) and OO (Bar
).
# this is package main (but you don't need to say so)
use strictures;
use DBI;
use Foo;
use Bar;
my $dbh = DBI->connect($dsn);
Foo::frobnicate($dbh, 1, 2)
my $bar = Bar->new(dbh => $dbh);
$bar->frobnicate(23);
package Foo;
use strictures;
sub frobnicate {
my ($dbh, $one, $two) = @_;
die q{No dbh given} unless $dbh; # could check ref($dbh)
$dbh->do( ... );
return;
}
package Bar;
use strictures;
sub new {
my ($class, %args) = @_;
die q{No dbh given} unless $args{dbh};
return bless \%args, $class;
}
sub frobnicate {
my ($self, $stuff) = @_;
$self->{dbh}->do(q{INSERT INTO bar SET baz=?}, undef, $stuff);
return;
}
__END__
How to pass arguments to a Perl test when run using Module::Build
Since one cannot set an environment variable in the same command line in Windows, I decided to change Build.PL for passing an environment variable to tests by subclassing Module::Build (Adding an action).
# Build.PL
use Module::Build;
use strict;
use warnings;
my $class = Module::Build->subclass(
class => "Module::Build::Husky",
code => <<'SUBCLASS' );
sub ACTION_test
{
my $self = shift;
my $dir = ($^O =~ /Win/) ? $ARGV[2] : $ARGV[1];
$ENV{HUSKYBINDIR} = $dir if(defined($dir) && $dir ne "" && -d $dir);
$self->SUPER::ACTION_test;
}
SUBCLASS
my $build = $class->new
(
...
);
$build->create_build_script;
Now when user runs
Build test user-supplied-directory
the user-supplied-directory
is passed to tests as $ENV{HUSKYBINDIR}
.
Strange enough that the same command-line argument is passed as $ARGV[1]
in Linux and as $ARGV[2]
in Windows. I had no opportunity to test it in FreeBSD and Mac OS X but hopefully, it will be $ARGV[1]
as in Linux.
How can I pass command-line arguments to a Perl program?
Depends on what you want to do. If you want to use the two arguments as input files, you can just pass them in and then use <>
to read their contents.
If they have a different meaning, you can use GetOpt::Std
and GetOpt::Long
to process them easily. GetOpt::Std
supports only single-character switches and GetOpt::Long
is much more flexible. From GetOpt::Long
:
use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose;
$result = GetOptions ("length=i" => \$length, # numeric
"file=s" => \$data, # string
"verbose" => \$verbose); # flag
Alternatively, @ARGV
is a special variable that contains all the command line arguments. $ARGV[0]
is the first (ie. "string1"
in your case) and $ARGV[1]
is the second argument. You don't need a special module to access @ARGV
.
Related Topics
Linux Sed Command - Using Variable with Backslash
How to Gzip All Files in All Sub-Directories into One Compressed File in Bash
Linux Cmd to Search for a Class File Among Jars Irrespective of Jar Path
Linux: Which Process Is Causing "Device Busy" When Doing Umount
New to Linux Kernel/Driver Development
Surround All Lines in a Text File with Quotes ('Something')
Find File Then Cd to That Directory in Linux
/Lib64/Ld-Linux-X86-64.So.2: No Such File or Directory Error
Emulating Linux Binaries Under MAC Os X
Trailing Arguments with Find -Exec {} +
How to Format My Grep Output to Show Line Numbers at the End of the Line, and Also the Hit Count
Comparing Two Unsorted Lists in Linux, Listing the Unique in the Second File
How I Should Run My Golang Process in Background
Error When Trying to Run .Asm File on Nasm on Ubuntu