perl get the path of a module inside the module? - perl

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

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

How to include perl modules while started from other directory?

I am writing some perl script and I want to include module. Everything is okay when I am in the same directory that my script.pl is. But when I try to start my script from other directory it says it cant locate my module. My includes looks like this:
use Functions qw(translateWord sendHelp);
and the file with module is called Functions.. I tried something like this:
use lib '..';
but it failed too.. I also tried:
use Cwd 'abs_path';
BEGIN {
my $dir = abs_path($0);
use lib "$dir";
}
but again it failed.. I also tried this:
use Cwd 'abs_path';
my $dir = abs_path($0);
use lib $dir;
and still fail.. I am new to Perl.
Thanks in advance!
The canonical way of accomplishing this is with 'use lib'. Using a lib of .. is not ideal though, because it's relative to the current working directory when you invoke the script.
The way to accomplish this is with FindBin.
E.g.
use FindBin;
use lib $FindBin::Bin."/../";
To traverse up a directory level from the 'base location' of your script.
Try this :
BEGIN{
unshift #INC, '/FULL/PATH/TO/DIR/OF/YOUR/MODULE';
}

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 Script Configuration and Relative Path Implementation on both Windows (XP) / Unix (Solaris)

Heylo,
I'm experiencing, somewhat of, a conundrum regarding perl script development. I have written a small Perl script using the standard (basic) Perl installation. I have the following setup:
C:\MyScript\perl.pl
C:\MyScript\configuration\config.ini
C:\MyScript\output\output.txt
This is the perl.pl source:
$config = '/configuration/config.ini';
$conf = Config::IniFiles->new( -file => $config_file );
$output_dir = conf->val('output', 'out_dir');
$output_file = "$output_dir/output.txt";
open (out, ">$output_file") || die ("It's not your day mate!");
print out "This is a test...";
close out;
This is the config.ini contents:
[output]
output_dir = C:\MyScript\output
The problem I am having is that the second line ($conf) appears to be having trouble opening the file at that location. As I will be using this script in both a windows and unix environment (without installing any addition modules) I was wondering how I could get around this? What I was hoping was to create a script that would entirely configurable through the config.ini file. The config however, only works if I give it's absolute path, like so:
$config = 'C:\MyScript\configuration\config.ini';
But since this will be deployed to several diverse environments modifying the scripts source is out of the question. What would you guys recommend? How should one approach such a scenario?
Any help and/or advice is greatly appreciated.
All the best,
MC
The problem lies in the $config assignment line -
$config = '/configuration/config.ini';
This searches for config.ini from the root directory due to the leading '/', interpreting the path as an absolute rather than relative. Try changing it to
$config = './configuration/config.ini';
This though will only work if you execute the perl script from the 'MyScript' directory. Have a look at the FindBin module for such cases or you could manipulate the $0 variable to get your perl script path.
Here is a solution to allways know which is your current directory and use your other dirs
use strict;
use warnings;
use FindBin;
use File::Spec;
use Cwd;
BEGIN {
$ENV{APP_ROOT} = Cwd::realpath(File::Spec->rel2abs($FindBin::Bin)) ;
}
#now you know your script directory,
#no matter from where your script is called
#if you have Modules specific for your script which are in
#a dir "lib" in the same dir as your script is
use lib (
"$ENV{APP_ROOT}/lib",
);
my $config = $ENV{APP_ROOT} . '/configuration/config.ini';
#Here is your script
#...
$output_file = "$ENV{APP_ROOT}/$output_dir/output.txt";
All Modules are from the CORE distribution so you have them installed.
Note that Windows accepts "/" slashes, so you can use them there too.
Cheers.