Perl - call scripts from another script with relative path - perl

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

Related

Including a module from another module [duplicate]

I don't know how to do one thing in Perl and I feel I am doing something fundamentally wrong.
I am doing a larger project, so I split the task into different modules. I put the modules into the project directory, in the "modules/" subdirectory, and added this directory to PERL5LIB and PERLLIB.
All of these modules use some configuration, saved in external file in the main project directory - "../configure.yaml" if you look at it from the module file perspective.
But, right now, when I use module through "use", all relative paths in the module are taken as from the current directory of the script using these modules, not from the directory of the module itself. Not even when I use FindBin or anything.
How do I load a file, relative from the module path? Is that even possible / advisable?
Perl stores where modules are loaded from in the %INC hash. You can load things relative to that:
package Module::Foo;
use File::Spec;
use strict;
use warnings;
my ($volume, $directory) = File::Spec->splitpath( $INC{'Module/Foo.pm'} );
my $config_file = File::Spec->catpath( $volume, $directory, '../configure.yaml' );
%INC's keys are based on a strict translation of :: to / with .pm appended, even on
Windows, VMS, etc.
Note that the values in %INC may be relative to the current directory if you put relative directories in #INC, so be careful if you change directories between the require/use and checking %INC.
The global %INC table contains an entry for every module you have use'd or require'd, associated with the place that Perl found that module.
use YAML;
print $INC{"YAML.pm"};
>> /usr/lib/perl5/site_perl/5.8/YAML.pm
Is that more helpful?
There's a module called File::ShareDir that exists to solve this problem. You were on the right track trying FindBin, but FindBin always finds the running program, not the module that's using it. ShareDir does something quite similar to ysth's solution, except wrapped up in a nice interface.
Usage is as simple as
my $filename = File::ShareDir::module_file(__PACKAGE__,
'my/data.txt');
# and then open $filename or whatever else.
or
my $dirname = File::ShareDir::module_dir(__PACKAGE__);
# Play ball!
Change your use Module call to require Module (or require Module; Module->import(LIST)). Then use the debugger to step through the module loading process and see where Perl thinks it is loading the files from.

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

Adding Modules Directory to #INC

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.

perl get the path of a module inside the module?

I have a perl module /x/y/z/test.pm. Inside this module, I want to read a config file /x/y/z/test.config. Yet, I am including my module from /a/b/c/mymain.pl. How can I get /x/y/z/ to build the path for /x/y/z/test.config in /x/y/z/test.pm?
Thanks,
AFAIK FindBin will show mymain.pl (and it might have been used in other modules, then the first invocation will win). Try __FILE__:
my $path = __FILE__;
$path =~ s/pm$/config/;

relative file paths in perl

I have a perl script which is using relative file paths.
The relative paths seem to be relative to the location that the script is executed from rather than the location of the perl script. How do I make my relative paths relative to the location of the script?
For instance I have a directory structure
dataFileToRead.txt
->bin
myPerlScript.pl
->output
inside the perl script I open dataFileToRead.txt using the code
my $rawDataName = "../dataFileToRead.txt";
open INPUT, "<", $rawDataName;
If I run the perl script from the bin directory then it works fine
If I run it from the parent directory then it can't open the data file.
FindBin is the classic solution to your problem. If you write
use FindBin;
then the scalar $FindBin::Bin is the absolute path to the location of your Perl script. You can chdir there before you open the data file, or just use it in the path to the file you want to open
my $rawDataName = "$FindBin::Bin/../dataFileToRead.txt";
open my $in, "<", $rawDataName;
(By the way, it is always better to use lexical file handles on anything but a very old perl.)
To turn a relative path into an absolute one you can use Cwd :
use Cwd qw(realpath);
print "'$0' is '", realpath($0), "'\n";
Start by finding out where the script is.
Then get the directory it is in. You can use Path::Class::File's dir() method for this.
Finally you can use chdir to change the current working directory to the directory you just identified.
So, in theory:
chdir(Path::Class::File->new(abs_path($0))->dir());
Relative paths are relative to the current working directory. If you don't have any control over the working directory then you need to find a more robust way to spcecify your file paths, e.g. use absolute paths, or perhaps relative paths which are relative to some specific location within the file system.