Why I am getting: Undefined subroutine - perl

I am trying to move existing perl files to a new server and I am getting an error (error_log file). It work on the old server.
When I access to: http://2x.29.xx.xx/admin/web.pl
I get an error:
[error] Undefined subroutine &web_main::main called at
/var/www/path/web.pl line 40.
web.pl file, it look something like this:
#!/usr/bin/perl -w
use strict;
use warnings;
use lib '/var/www/web';
our $var = '/var/www/web';
our $var1;
our $var2;
use web_main qw($var $var1 $var2);
web_main::main(); # Line 40
web_main.pm file (in /var/www/web), look something like this:
#!/usr/bin/perl -w
package web_main;
use strict;
use warnings;
sub main
{
# Lots of code here... removed for brevity
}
Not sure what went wrong?

I'm not really an expert in Perl but perhaps you need to export the main subroutine? You can use Exporter.

Related

Undefined subroutine &main::key_enc

I have a Perl module named McuEnc.pm which is located at /home/eric/temp directory:
package McuEnc;
use 5.010;
use strict;
use warnings;
use Exporter qw(import);
our #EXPORT_OK = qw(key_enc data_enc data_dec);
sub key_enc { some code }
sub data_enc { some code }
sub data_dec { some code }
1;
I have a Perl script named test.pl which is also located at /home/eric/temp directory:
use 5.010;
use strict;
use warnings;
use McuEnc qw(key_enc data_enc data_dec);
key_enc("1111");
...
I have changed working directory to /home/eric/temp. Now if I run test.pl script, it runs correctly. But if I create a new directory /home/eric/temp/My and move the McuEnc.pm file into it and then modify the test.pl script as follwoing:
use 5.010;
use strict;
use warnings;
use My::McuEnc qw(key_enc data_enc data_dec);
key_enc("1111");
...
then I'm getting the following error when I run test.pl again:
Undefined subroutine &main::key_enc called at ./test line 6.
However, I can still call the subroutine correctly with:
McuEnc::key_enc("1111");
So why I'm getting the error message? what is wrong?
This was answered by Sobrique in the comments, but it was never posted as an official answer:
"The use statement doesn't match the package statement. Try package My::McuEnc instead"

Calling script with the same #INC as the parent script

I want to call an external script with system ($script) or do $script. In my #INC i have some specific modules which I import. How can I call the $script and transfer it the same #INC?
Script1.pm
#importing some libs
#code
$script = "path_to_script";
system ($script);
Script2.pm
use LibFromScript1#INC;
And I get the Error :
Can't locate LibFromScript1 in #INC...
The easiest way is probably to set the PERL5LIB environment variable. That will add a list of directories to the child process's #INC array
Your code would look something like
$ENV{PERL5LIB} = join ':', #INC;
system $script;
This has the disadvantage that the standard directories will also be added to #INC. It shouldn't cause any problems, but it would be best to set PERL5LIB to just the custom directories if you know them at that point.
Note also that perl will ignore PERL5LIB if you are running under the taint flag.
Strictly answering your question you could do $script; although recommended way would be to separate your common program logic into module, and use/require it.
You could use Storable to save #INC in a file and then pick it in other script.
For example you could do something like below.
test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Storable;
store (\#INC, "test2.dump") or die "could not store";
system("perl", "test2.pl", $$) == 0 or die "error";
test2.pl
#!/usr/bin/perl
use strict;
use warnings;
use Storable;
use Data::Dumper;
my $parentpid = shift;
my $ref = retrieve("test2.dump") or die "couldn't retrieve";
print Dumper $ref;
Once you get the #INC in test2.pl as $ref, you can modify #INC in test2.pl to take contents from $ref.

Perl throws undefined subroutine in module instead of correct error

I've moved part of a script into a module, and now the only error I get when I do something wrong is "Undefined subroutine", even when the real error is that I misspelled a variable, or forgot a closing paren, or left off a semi-colon.
The only way to find the real error is to copy the entire thing into a script and run it that way. It's very tedious. Am I doing something wrong, or is this just the way modules are supposed to work?
Here is a very simple example that shows the problem:
Module:
#!/usr/bin/env perl
package CalledError;
use Exporter qw(import);
our #EXPORT_OK=qw(do_build_stor_pools);
use strict;
use warnings;
use feature qw(say);
sub do_build_stor_pools {
say "now in CalledError do_build_stor_pools";
#my $undef_var="uncomment this to fix";
say $undef_var;
return;
}
Calling script:
#!/usr/bin/env/perl
use strict;
use warnings;
my $buildstor_mod="CalledError";
eval "require $buildstor_mod";
$buildstor_mod->import();
CalledError::do_build_stor_pools();
Run it like this to get
Undefined subroutine &CalledError::do_build_stor_pools called at calling_test.pl line 11.
Uncomment the definition of $undef_var to make it work.
If you checked $EVAL_ERROR, you would see the real error:
#!/usr/bin/env/perl
use strict;
use warnings;
my $buildstor_mod="CalledError";
eval "require $buildstor_mod";
if ($#) {
die "$#";
}
$buildstor_mod->import();
CalledError::do_build_stor_pools();
Error message:
Global symbol "$undef_var" requires explicit package name at CalledError.pm line 15.
Compilation failed in require at (eval 1) line 2.
You see the undefined subroutine error because require fails.
You're not checking the error condition of your eval:
eval "require $buildstor_mod";
die $# if $#;
However, to load a module, you should just use use:
use strict;
use warnings;
use CalledError qw(do_build_stor_pools);
do_build_stor_pools();
Outputs:
Global symbol "$undef_var" requires explicit package name at CalledError.pm line 14.

Why doesn't my Perl script use my module?

module.pm
package module;
use 5.012;
use warnings;
sub Parse
{
return 1;
}
1;
script.pl
#!/usr/bin/perl -w
use 5.012;
use warnings;
use lib 'C:/';
use module;
print Parse("value");
Stdout
Undefined subroutine &main::Parse
You need either to write:
print module::Parse("value");
or to change the module package to export the name Parse.
See http://perldoc.perl.org/perlmod.html#Perl-Modules for guidance in exporting symbols from your module.
(By the way, you should really name your module Module rather than module. Lowercase module-names are used for Perl built-in features like use warnings and use strict.)
Several things:
First, use Local as your module prefix. That way, if you just happen to have a module with the same name in your Perl installation, it will use yours. Call it "Local::Module". Then, create a Local directory, and name your module Module.pm.
The other thing you have to understand is that you define your module in another namespace. By default, everything is in the main namespace until you use the package statement. That creates another namespace that your package uses. This way, if your package has a function foo, and you've defined a function foo in your main program, they won't collide.
Thus, you have two choices: One (the preferred now) is to simply call your subroutine with the full package name prepended to it. The second is to export your subroutine names to your main program. This can cause problems with duplicate names, but you don't have to keep typing in the package name every time you call your subroutine.
Without Exporting the name
Local/Module.pm
# /usr/bin/env perl
# Local/Module.pm
package Local::Module;
use strict;
use warnings;
sub Parse {
my $value = shift; #Might as well get it.
print "I got a value of $value\n";
return $value;
}
1; #Need this or the module won't load
program.pl
# /usr/bin/env perl
# program.pl
use strict;
use warnings;
use Local::Module;
Local::Module::Parse("Foo");
With export:
Local/Module.pm
# /usr/bin/env perl
# Local/Module.pm
package Local::Module;
use strict;
use warnings;
use Exporter qw(import);
our #EXPORT_OK(Parse); #Allows you to export this name into your main program
sub Parse {
my $value = shift; #Might as well get it.
print "I got a value of $value\n";
return $value;
}
1; #Need this or the module won't load
program.pl
# /usr/bin/env perl
# program.pl
use strict;
use warnings;
use Local::Module qw(Parse);
Parse("Foo");

Perl problem 'require' the same file

I have a shared module in perl. The main program needs two files, first, a shared file (let's call it 'X'), and, second, a 'package' file. File 'X' is also included in the 'package' file using 'require'. When I compile this program it gives me the following error:
Undefined subroutine &main::trim called at testing.pl line 8.
My understanding is that perl couldn't find the trim() module. If I don't include the package file, then this will run without any problem.
Can anyone shed light on this problem?
These are my codes:
Main program: testing.pl
#!/usr/bin/perl -w
use strict;
use postgres;
require "shared.pl";
trim("als");
Package File: postgres.pm
#!/usr/bin/perl
package postgres;
use strict;
use DBI;
require "shared.pl";
1;
shared file: shared.pl
#!/usr/bin/perl
# =============
# shared module
# =============
use strict;
sub trim($)
{
}
1;
If the module doesn't use package, you want do instead of require. See What is the difference between library files and modules?.
do "shared.pl" or die $#;
You really should create a proper module, one with a package statement.
package Shared;
use strict;
use warnings;
our #EXPORT = qw( trim );
use Exporter qw( import );
sub trim { ... }
1;
Name the file Shared.pm and load it using use Shared;.
By default, require will only load a file one time. In this case, that one time is from the file postgres.pm, in the postgres package. So the trim subroutine gets defined in the postgres namespace as &postgres::trim.
One workaround would be to use the fully qualified subroutine name in the testing.pl file:
postgres::trim("als"); # not trim("als")
Another workaround is to hack the %INC table (the variable that keeps track of what modules/files have already been use'd and require'd) so you can reload shared.pl into the main package:
use postgres;
delete $INC{"shared.pl"};
require "shared.pl";
A third workaround would be to export the trim function from the postgres package to the main package. The docs for the Exporter module are a good introduction to why and how this is done.
# in postgres.pm
*main::trim = *trim;
# or in testing.pl
*trim = *postgres::trim;
trim("als");