Calling script with the same #INC as the parent script - perl

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.

Related

Perl - Relative path of a file inside a module

I am building up a website using Perl. I organized my files as follow:
/index.cgi
/perl/modules/databaseFunctions.pm
/perl/indexCheck.cgi
/database/database.xml
Inside databaseFunctions.pm I have a function X() that reads and writes on database.xml. In X() I have specified relative path of the database as follow:
sub X{
my $db_path='../../database/database.xml';
my $parser=XML::LibXML->new();
my $doc=$parser->parse_file($db_path);
....
....
}
Here is the problem:
I have to call X() from index.cgi and indexCheck.cgi but I get an error the following error:
Could not create file parser context for file "../../database/database.xml": No such file or directory at perl/modules/databaseFunctions.pm line 21.
I think the problem is that when I call X() inside index.cgi or inside /perl/indexCheck.cgi the relative path of the database is different but I don't know how to set a path that works for index.cgi and /perl/indexCheck.cgi.
I think the problem boils down to "How to find out the path of the current script (*.pl)?" and
"How to find out the path of the current module (*.pm)?".
Scripts
For scripts, there is a very convenient module, FindBin, that offers 4 variables for the current script's name and path with
either symlinks resolved or not. Usually $FindBin::Bin is what you are looking for. It's the path of the current script.
I often use it to enhance the #INC path so that my scripts find additional (own) modules like so:
use FindBin;
use lib "$FindBin::Bin/my_mod_path";
use MyModule;
In this case MyModule.pm is searched for in the directory my_mod_path below the current script's path. Very convenient.
The module is part of the core distribution, i.e. no further installation is neccessary.
Modules
FindBin may not safely be used from inside modules because then it depends who (script or module) makes the first use FindBin;.
So if you don't want to care about the order, don't use FindBin; in modules, only in scripts.
For modules, there is some trick. Use the perl function caller().
Depending on the context called in, it returns the $filename of the file where it actually was called.
Thus, in modules you can safely use the following to get the module's path:
use File::Basename;
my $path_of_this_module = File::Basename::dirname( eval { ( caller() )[1] } );
Given that path you can navigate relative to it in order to find the other files you need, e.g. "$path_of_this_module/../.." and so on.
EDIT
index.cgi:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/perl/modules";
use databaseFunctions;
databaseFunctions::X( "called from index.cgi\n" );
perl/indexCheck.cgi:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/modules";
use databaseFunctions;
databaseFunctions::X( "called from indexCheck.cgi\n" );
perl/modules/databaseFunctions.pm:
package databaseFunctions;
use File::Basename;
my $path_of_this_module = File::Basename::dirname( eval { ( caller() )[1] } );
sub X {
my $arg = shift;
my $db_path="$path_of_this_module/../../database/database.xml";
open(my $fh, '>>', $db_path) or die "cannot open $db_path: $!\n";
print $fh $arg;
close($fh);
}
1;
When I now call ./index.cgi and then ./perl/indexCheck.cgi, then I get the following:
database/database.xml:
called from index.cgi
called from indexCheck.cgi
Exactly, what I thought you were looking for.

Perl -How do i pass complete directory path name into perl script

I have a perl script and its been executing from "/root/pkt/sw/se/tool" path and would need the complete directory path inside the script.
Can you please let me know how would i get the complete path?
sample.pl
our ($tool_dir);
use strict;
use warnings;
BEGIN {
$tool_dir = $0;
my $home_path = $tool_dir
$home_path =~ s|\w*/\w*/\w*$||;
my $target_path ="obj/Linux-i686/usr/share/ddlc/lib";
$lib_dir = "$home_path$target_path";
unshift(#INC, $lib_dir);
}
And i am executing this script from "pkt/sw/se/tool" path but here i am getting only "pkt/sw/se/tool" instead of "/root/pkt/sw/se/tool"
my perl script is available under /root/pkt/sw/se/tools/sample.pl
You can use the CWD module (http://perldoc.perl.org/Cwd.html) (code take from that page)
use Cwd;
my $dir = getcwd;
use Cwd 'abs_path';
my $abs_path = abs_path($file);
or you could execute the pwd command
$cwd = `pwd`;
If you just want the directory, not the full path, you could check out an existing answer at Print current directory using Perl
Use one of the modules already mentioned, never use backticks - unless you fully understand the risks and implications of doing so. If you do want to run 'pwd' then call it via something like IPC::Run3.
Examples:
#!/usr/bin/perl
use strict;
use warnings;
use Cwd;
use IPC::Run3;
# CWD
my $working_dir_cwd = getcwd;
print "Woring Dir (Cwd): $working_dir_cwd\n";
# IPC::Run3
my ($in, $out, $err);
my #command = qw/pwd/;
run3 \#command, $in, \$out, \$err or die $?;
print "Working Dir (pwd): $out\n";
You can use FindBin to locate directory of original perl script.
use FindBin;
use lib "$FindBin::Bin/../../../obj/Linux-i686/usr/share/ddlc/lib";

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");

How do I load libraries relative to the script location in Perl?

How can you get current script directory in Perl?
This has to work even if the script is imported from another script (require).
This is not the current directory
Example:
#/aaa/foo.pl
require "../bbb/foo.pl"
#/bbb/bar.pl
# I want to obtain my directory (`/bbb/`)
print($mydir)
The script foo.pl could be executed in any ways and from any directory, like perl /aaa/foo.pl, or ./foo.pl.
What people usually do is
use FindBin '$Bin';
and then use $Bin as the base-directory of the running script. However, this won't work if you do things like
do '/some/other/file.pl';
and then expect $Bin to contain /some/other/ within that. I'm sure someone thought of something incredibly clever to work this around and you'll find it on CPAN somewhere, but a better approach might be to not include a program within a program, but to use Perl's wonderful ways of code-reuse that are much nicer than do and similar constructs. Modules, for example.
Those generally shouldn't care about what directory they were loaded from. If they really need to operate on some path, you can just pass that path to them.
See Dir::Self CPAN module. This adds pseudo-constant __DIR__ to compliment __FILE__ & __LINE__.
use Dir::Self;
use lib __DIR__ . '/lib';
I use this snippet very often:
use Cwd qw(realpath);
use File::Basename;
my $cwd = dirname(realpath($0));
This will give you the real path to the directory containing the currently running script. "real path" means all symlinks, "." and ".." resolved.
Sorry for the other 4 responses but none of them worked, here is a solution that really works.
In below example that adds the lib directory to include path the $dirname will contain the path to the current script. This will work even if this script is included using require from another directory.
BEGIN {
use File::Spec;
use File::Basename;
$dirname = dirname(File::Spec->rel2abs( __FILE__ )) . "/lib/";
}
use lib $dirname;
From perlfaq8's answer to How do I add the directory my program lives in to the module/library search path?
(contributed by brian d foy)
If you know the directory already, you can add it to #INC as you would for any other directory. You might if you know the directory at compile time:
use lib $directory;
The trick in this task is to find the directory. Before your script does anything else (such as a chdir), you can get the current working directory with the Cwd module, which comes with Perl:
BEGIN {
use Cwd;
our $directory = cwd;
}
use lib $directory;
You can do a similar thing with the value of $0, which holds the script name. That might hold a relative path, but rel2abs can turn it into an absolute path. Once you have the
BEGIN {
use File::Spec::Functions qw(rel2abs);
use File::Basename qw(dirname);
my $path = rel2abs( $0 );
our $directory = dirname( $path );
}
use lib $directory;
The FindBin module, which comes with Perl, might work. It finds the directory of the currently running script and puts it in $Bin, which you can then use to construct the right library path:
use FindBin qw($Bin);
You can also use local::lib to do much of the same thing. Install modules using local::lib's settings then use the module in your program:
use local::lib; # sets up a local lib at ~/perl5
See the local::lib documentation for more details.
Let's say you're looking for script.pl. You may be running it, or you may have included it. You don't know. So it either lies in the %INC table in the first case or as $PROGRAM_NAME (aka $0) in the second.
use strict;
use warnings;
use English qw<$PROGRAM_NAME>;
use File::Basename qw<dirname>;
use File::Spec;
use List::Util qw<first>;
# Here we get the first entry that ends with 'script.pl'
my $key = first { defined && m/\bscript\.pl$/ } keys %INC, $PROGRAM_NAME;
die "Could not find script.pl!" unless $key;
# Here we get the absolute path of the indicated path.
print File::Spec->rel2abs( dirname( $INC{ $key } || $key )), "\n";
Link to File::Basename, File::Spec, and List::Util

Perl 'system' failure messages

Say I have this perl "program" called simple.pl:
#!/usr/bin/perl
use xyz; # xyz is bogus and doesn't exist
And I also have this "program", called simple2.pl:
#!/usr/bin/perl
system("simple.pl");
my $abc = `simple.pl`;
printf("abc %s\n", $abc);
for both system and backtick, I get this message:
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 7.
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 9.
Not very useful for the user calling simple2.pl. Is there a way to get a more useful message?
Note. simple.pl does exist in the current directory. The real problem is that simple.pl doesn't compile. simple2 responds by saying simple doesn't exist. it's a misleading message.
If I had a way to even capture the compile message that would be a start.
This means system couldn't find an executable named "simple.pl" on your PATH. If your simple.pl is in the current directory, you could try to change "simple.pl" to "./simple.pl".
Actually, I don't see how to make this message more descriptive. If you were perl, how would you report this error?
BTW, I wouldn't try to run "simple2.pl" from inside of simple2.pl :)
Yes, check to see if the file exists and is executable, and if it isn't, print a more descriptive message.
unless (-ex $filename) {
print "I am unable to execute file $filename.";
}
If perl say it can't find the file, then it can't find the file. And the problem is more your code. Look at this example.
sidburn#sid:~/perl$ cat test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use xyz;
sidburn#sid:~/perl$ cat test2.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('test.pl');
sidburn#sid:~/perl$ cat test3.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('./test.pl');
If you execute test2.pl you get:
sidburn#sid:~/perl$ ./test2.pl
Can't exec "test.pl": No such file or directory at ./test2.pl line 4.
If you execute test3.pl you get:
sidburn#sid:~/perl$ ./test3.pl
Can't locate xyz.pm in #INC (#INC contains: /home/sidburn/perl510/lib/5.10.1/i686-linux /home/sidburn/perl510/lib/5.10.1 /home/sidburn/perl510/lib/site_perl/5.10.1/i686-linux /home/sidburn/perl510/lib/site_perl/5.10.1 .) at ./test.pl line 4.
BEGIN failed--compilation aborted at ./test.pl line 4.
If you don't provide a relative or absolute path then perl lookup the command in your $PATH environment variable. If it is not there it can't find the file.
You need to provide "./" if it is in the current directory. But note "current directory" doesn't mean the directory where your script relies.
If you want the later then you probably want to do a
use FindBin;
with this you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec::Functions;
my $exe = catfile($FindBin::RealBin, 'test.pl');
print $exe, "\n";
system($exe);
if you want to check if system returns correctly, you need to check the return value from the system() command or $? later that holds the value.
if ( $? != 0 ) {
die "Cannot execute $exe.\n";
}
if you want to suppress messages from your program you need to redirect STDOUT, STDERR before starting your program with system().
Or use something like IPC::System::Simple
Or IPC::Open3 (in the core).
Bonus points for enabling the warnings pragma! Have an upvote!
You want to use backticks or qx// to capture the output of an external program, not system. To substitute your own error message that will make sense to your users (more points for you!), then you might do something as in
#! /usr/bin/perl
use strict;
use warnings;
no warnings 'exec';
chomp(my $abc = `simple2.pl`);
if ($? == 0) {
printf("abc %s\n", $abc);
}
else {
die "$0: unable to calculate abc\n";
}
In case you're unfamiliar, $? is
$CHILD_ERROR
$?
The status returned by the last pipe close, backtick command, successful call to wait or waitpid, or from the system operator.
When $? is zero, it indicates success.
Remember that the warnings pragma is lexical, so rather than disabling the warning for the whole program, you might do it for just one sub:
sub calculate_abc {
no warnings 'exec';
# ...
}
If you are trying to execute something you know is a Perl script, why not invoke the interpreter directly rather than dealing with the system knowing how to execute the file?
my $file = 'simple.pl';
-e $file or die "file '$file' not found";
system "perl $file";
# or
print `perl $file`;
to run with the same installation of perl that is running your current script:
system "$^X $file"; # or `$^X $file`
$^X is a special Perl variable that contains the file name of the running interpreter.
I had the exact same issue and figured out that perl wasn't installed. So the bash script was trying to execute the perl without an interpreter.
ls /usr/bin/perl
Try specifying the full path to the "simple.pl" file.