Adding Modules Directory to #INC - perl

I have a bunch of scripts that I need to run them in sequential manner somthing like the below
foreach my (qw (script1.pl script2.pl script3.pl ){
my $script=File::Spec->catfile($Bin,'Scripts',$_);
system "$^X $script";
}
every of those scripts use 2 modules which located under lib library where the scripts found i.e
i have project library which contian the above script and 2 directory one for my modules and the other for the scripts that the above script run,the question how can i add the modules directory to the #INC path in dynamic manner on the above script that when I run the system code it will find the lib directory on the #INC path .I tried to use the following :
BEGIN{
use lib File::Spec->catdir($Bin,'LIB');
}
but its not works any idea?
The error I get for every script on the loop
"BEGIN failed--compilation aborted at C:\Porject\Scripts\script1.pl line 4
Can't locate Detection.pm in #INC"

It's not clear WHERE you're manipullating #INC, but the way I read your question, it seems you did it in script.pl.
Simply changing #INC of parent script will not affect #INC of scripts you call via system() call, since those child scrips will have their own copy of Perl interpreter.
You need to manipulate it in the called scripts (script1.pl, script2.pl, script3.pl).
You can do it 1 of 3 ways:
By adding "-I" Perl parameter when calling the script from script.pl:
my $lib_path = File::Spec->catdir($Bin,'LIB');
system "$^X -I $lib_path $script";
Benefits: You only put the code in 1 script (parent one).
Because of that, this is the solution I would recommend.
By having script1.pl (and 2 and 3) adjust its own #INC based on script path.
For example:
use Cwd qw(abs_path);
use FindBin;
use lib abs_path("$FindBin::Bin/../LIB");
Benefit: You don't need to know ehere "$Bin" is. Downside: you need to edit every one of those scripts.
By having script1.pl (and 2 and 3) adjust its own #INC based on the logic you tried to put into parent script:
# Add to scriptX.pl - all of them.
BEGIN { use lib File::Spec->catdir($Bin,'LIB'); }
Downside 1: you need to edit every one of those scripts. Downside 2: You need to know where "$Bin" is in every one of those scripts.

Related

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

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.

Perl - call scripts from another script with relative path

I have Perl script that call other Perl scripts, I use this line for that :
system($^X, "script.pl", #ARGV);
All scripts exists on same folder but I want to call the main one from another folder, meaning scripts are under D:\TEST\Perl but I open command line from C:\ and call the main one from this location.
Probably a Silly question, but how can I call the child scripts that their location is relative to the main one?
Do I really need to use module for that? Which one?
Sounds like you want the FindBin module
use FindBin '$Bin'; # $Bin will contain the directory containing the executable file
# Then, later in your code
system($^X, "$Bin/script.pl", #ARGV);

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.

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...