My Perl code does not seem to work with .lib? - perl

I'm new to both Perl and its feature of .lib.
I made this simple subroutine and saved it as a file with an extension of .lib
sub shorterline {
print "Content-type: text/html\n\n";
}
1;
As I tried to insert the subroutine into the Perl file with an extension of .cgi below, it doesn't work somehow:
#!/usr/bin/perl
require 'mysubs.lib';
&shorterline;
print "Hello, world!";
I gave the .cgi the chmod permission, but the .cgi still doesn't work, what seem to be the problem ?

Your descriptions of what the problem is are rather unclear.
it doesn't work somehow
the .cgi still doesn't work
Without knowing what problems you're seeing, it's hard to know what the problem is. But I tried copying your code and running the program from the command line and I got this error message:
Can't locate mysubs.lib in #INC (#INC contains: ...)
So I think you are using a recent version of Perl and are running up against this change:
Removal of the current directory (".") from #INC
The perl binary includes a default set of paths in #INC. Historically it has also included the current directory (".") as the final entry, unless run with taint mode enabled (perl -T). While convenient, this has security implications: for example, where a script attempts to load an optional module when its current directory is untrusted (such as /tmp), it could load and execute code from under that directory.
Starting with v5.26, "." is always removed by default, not just under tainting. This has major implications for installing modules and executing scripts.
If this is the problem, then you can fix it by adding the script's directory to #INC as follows:
use FindBin qw( $RealBin );
use lib $RealBin;
before your call to require. If that doesn't solve your problem, perhaps you would consider sharing a little more detail about the problems that you are experiencing.

Related

Using a Perl module in my own directory [duplicate]

I have a module in the parent directory of my script and I would like to 'use' it.
If I do
use '../Foo.pm';
I get syntax errors.
I tried to do:
push #INC, '..';
use EPMS;
and .. apparently doesn't show up in #INC
I'm going crazy! What's wrong here?
use takes place at compile-time, so this would work:
BEGIN {push #INC, '..'}
use EPMS;
But the better solution is to use lib, which is a nicer way of writing the above:
use lib '..';
use EPMS;
In case you are running from a different directory, though, the use of FindBin is recommended:
use FindBin; # locate this script
use lib "$FindBin::RealBin/.."; # use the parent directory
use EPMS;
There are several ways you can modify #INC.
set PERL5LIB, as documented in perlrun
use the -I switch on the command line, also documented in perlrun. You can also apply this automatically with PERL5OPT, but just use PERL5LIB if you are going to do that.
use lib inside your program, although this is fragile since another person on a different machine might have it in a different directory.
Manually modify #INC, making sure you do that at compile time if you want to pull in a module with use. That's too much work though.
require the filename directly. While this is possible, it doesn't allow that filename to load files in the same directory. This would definitely raise eyebrows in a code review.
Personally I prefer to keep my modules (those that I write for myself or for systems I can control) in a certain directory, and also to place them in a subdirectory. As in:
/www/modules/MyMods/Foo.pm
/www/modules/MyMods/Bar.pm
And then where I use them:
use lib qw(/www/modules);
use MyMods::Foo;
use MyMods::Bar;
As an aside.. when it comes to pushing, I prefer the fat-arrow comma:
push #array => $pushee;
But that's just a matter of preference.
'use lib' is the answer, as #ephemient mentioned earlier. One other option is to use require/import instead of use. It means the module wouldn't be loaded at compile time, but instead in runtime.
That will allow you to modify #INC as you tried there, or you could pass require a path to the file instead of the module name. From 'perldoc -f require':
If EXPR is a bareword, the require assumes a ".pm" extension and
replaces "::" with "/" in the filename for you, to make it easy to
load standard modules. This form of loading of modules does not risk
altering your namespace.
You have to have the push processed before the use is -- and use is processed early. So, you'll need a BEGIN { push #INC, ".."; } to have a chance, I believe.
As reported by "perldoc -f use":
It is exactly equivalent to
BEGIN { require Module; import Module LIST; }
except that Module must be a bareword.
Putting that another way, "use" is equivalent to:
running at compile time,
converting the package name to a file name,
require-ing that file name, and
import-ing that package.
So, instead of calling use, you can call require and import inside a BEGIN block:
BEGIN {
require '../EPMS.pm';
EPMS->import();
}
And of course, if your module don't actually do any symbol exporting or other initialization when you call import, you can leave that line out:
BEGIN {
require '../EPMS.pm';
}
Some IDEs don't work correctly with 'use lib', the favored answer. I found 'use lib::relative' works with my IDE, JetBrains' WebStorm.
see POD for lib::relative
The reason it's not working is because what you're adding to #INC is relative to the current working directory in the command line rather than the script's directory.
For example, if you're currently in:
a/b/
And the script you're running has this URL:
a/b/modules/tests/test1.pl
BEGIN {
unshift(#INC, "..");
}
The above will mean that .. results in directory a/ rather than a/b/modules.
Either you must change .. to ./modules in your code or do a cd modules/tests in the command line before running the script again.

I want to create a word count module and I want to reuse it further

I want to create a module in Perl. The below code is not working properly. I want to create a word count module and I want to reuse it further. Can anyone help me out to create this module? This is my first attempt to create a module so kindly help me out.
package My::count
use Exporter qw(import);
our #Export_ok = qw(line_count);
sub line_count {
my $line = #_;
return $line;
}
I saved the above code in count.pm
use My::count qw(line_count);
open INPUT, "<filename.txt";
$line++;
print line count is $line \n";
I saved the above script in .pi extension.
This code is showing error when I run it on an Ubuntu platform. Kindly help me to fix this errors.
Perl scripts are stored with .pl extension. As you say use My::count qw(line_count); Perl tries to search the modules from the directories stored in #INC variable. You can run it with the -I flag to specify the directory to search the custom packages. Refer to this question for more info.
By convention Perl packages usually have a capitalized first letter, so My::count is more in keeping with convention if you call it package My::Count;. Typically lower-cased module names are reserved for pragmas such as 'strict' and 'warnings'. So go ahead and change the name to My::Count.
Next, save the module in a path such as lib/My/Count.pm. lib is by convention as well.
Then you have to tell your script where to find package My::Count.
Let's assume you're storing your module and your executable like this:
~/project/lib/My/Count.pm
~/project/bin/count.pl
Notice I also used a .pl extension for the executable. This is another convention. Often on Unix-like systems people omit the .pl extension altogether.
Finally, in your count.pl file you need to tell perl where to find the library. Often that is done like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use My::Count 'line_count';
# The rest goes here...
As you can see, we're using FindBin to locate where the executable is stored, and then telling perl that it should look (among other places) in the lib folder stored in a relative location to the executable.
Naturally, as this is Perl, this is not the only way to do it. But it's one common idiom.
You need to move your count.pm file into a directory called My. So you have the following.
./count.pl
./My/count.pm

Apache doesn't see pm file

I have installed Apache 2.2 and strawberry perl. I have textxx.pm. And I prepare simple script
#!/usr/bin/perl
use textxx;
print("HelloWorld!");
When I run it via cmd it works. When I run it via web browser I get and error:
Can't locate textxx.pm in #INC (#INC contains: C:/strawberry/perl/site/lib C:/strawberry/perl/vendor/lib C:/strawberry/perl/lib .)
It's weird because script without "use" works.
My module is located at the same directory as script
You are trying to use the textxx.pm perl module.. but it is not found while running through apache, if you remove the use line it will obviously work since it's not looking for the module.
a quick fix, if your textxx.pm is in let's say C:/strawberry/perl/site/lib then just add this at the top of your script:
#!/usr/bin/perl
use lib "C:/strawberry/perl/site/lib";
use textxx;
print("HelloWorld!");
Lack of permission could be the issue, but I'm betting the problem is that you're expecting Perl to look in the directory containing the script for the module. Perl doesn't do that, at least not by default.
It worked from the console because you set the current work directory happened to the be the same as the script, but that's not the case when you're running it through apache.
To tell Perl to look in the same directory as the script, add the following because use textxx;:
use Cwd qw( realpath );
use File::Basename qw( dirname );
use lib dirname(realpath($0));

What's the canonical way to make the directory that has the current source file to be searched by `use` and `require` statements in Perl in mod_perl?

I tried
BEGIN {
unshift #INC, 'current_path_string';
}
But it only works for use, when require, it's not searched.
Is there a work around?
When running under mod_perl, once the server is up #INC is frozen and cannot be updated. The only opportunity to temporarily modify #INC is while the script or the module are loaded and compiled for the first time. After that its value is reset to the original one. The only way to change #INC permanently is to modify it at Apache startup.
Two ways to alter #INC at server startup:
In the configuration file. e.g PerlSetEnv PERL5LIB /home/httpd/perl
In the startup file directly alter the #INC and load the startup file from the configuration file.
See also #INC and mod_perl
Yes, you can update #INC in the startup script. But using the code below in your module will simply work:
use lib '/app/my-libs';
at least - for my CGI app running under mod_perl.
use Foo;
is the same as
BEGIN {
require Foo;
import Foo;
}
so if it works for use, it works for require.

How to reuse the functions defined in one script from another in Perl, running as cgi under Apache?

I'm trying to organize a few perl scripts in a way so I can reuse some functions between them. The directive 'require' however doesn't seem to work when the script is ran as CGI under Apache. I've tried this:
require "common.pl"
but it doesn't work under Apache.
I've also tried this:
use File::Basename;
use Cwd qw(abs_path);
require abs_path(dirname($0))."/common.pl";
which works both on the command line and under Apache on my local server but not on my webhost's server.
Do you know what the proper way to 'require' a perl script is so it works on both the command line and under Apache?
Edit: I'm looking for a way (not necessarily using 'require') to make the functions defined in 'common.pl' available to the script calling this here. So basically I have 2 scripts 'foo.pl' and 'bar.pl' from which I'd like to reuse the functions written in 'common.pl'. What is the right way to do that in Perl? 'require' works fine on the command line, but not under Apache... I have no control over %INC, and I can't hardcode the full path to 'common.pl'.
I can't hardcode the path to 'common.pl' because this set of scripts has to run on 3 different servers, each having a different absolute path for it.
Edit2: The error message I get happens when running using mod_perl (I was mistakenly stating this was all happening when running under Apache/cgi, but I had mod_perl on on one of the 3 setups, the other 2 setups were running scripts as regular cgi scripts, and the error I was seeing was unrelated to the 'require' statement, which works fine under regular cgi). The error is as follows:
Can't locate common.pl in #INC (#INC contains: /Library/Perl/Updates/5.10.0/darwin-thread-multi-2level /Library/Perl/Updates/5.10.0 /System/Library/Perl/5.10.0/darwin-thread-multi-2level /System/Library/Perl/5.10.0 /Library/Perl/5.10.0/darwin-thread-multi-2level /Library/Perl/5.10.0 /Network/Library/Perl/5.10.0/darwin-thread-multi-2level /Network/Library/Perl/5.10.0 /Network/Library/Perl /System/Library/Perl/Extras/5.10.0/darwin-thread-multi-2level /System/Library/Perl/Extras/5.10.0 . /usr) at /.../check.pl
Configuring software to suit different deployments is a fact of life. Sometimes 'magic' can help, but often it can lead to inpenetrable problems as well.
Do you have control over the environment? I would set environment variables (http://httpd.apache.org/docs/current/mod/mod_env.html) in your Apache config and use them as the path in your require.
Why would hardcoding three different paths be a problem?
use Sys::Hostname;
my $host = hostname();
$host eq 'foo' and push #INC, '/foo';
$host eq 'bar' and push #INC, '/bar';
$host eq 'baz' and push #INC, '/baz';
require 'common.pl';
Granted, it's not the most elegant solution, but if it works...