Perl `dirname` prints dot instead of full path - perl

I am trying to implement a perl script and I need to get a directory of my executed file.
For example if my file that I'm executing is at C:\Scripts\MyScript.pl I would like to get C:\Scripts.
I tried dirname but it does not seem to do what i want. It just prints .
I understand that I need to use dirname and abs_path together, however abs_path seems buggy to me because it returns some UNIX looking directory and then concatenates it with the actual path which obviously is invalid path in the end.
Here is my proof of concept.
# The initial variable with file
say $0; # C:\Users\sed\AppData\Roaming\npm\node_modules\my-test-module\my-test-file
# This is just a current directory
say dirname($0); # .
# This looks like a bug to me, the shell is opened in C:/Users/sed so it is related
say abs_path($0); # /c/Users/sed/C:/Users/sed/AppData/Roaming/npm/node_modules/my-test-module\my-test-file
# This is what I need, except I don't need that first UNIX looking part
say dirname(abs_path($0)); # /c/Users/sed/C:/Users/sed/AppData/Roaming/npm/node_modules/my-test-module

A quick look at:
perldoc File::Basename
shows why it may not be the best choice for what you need to do.
Please try File::Spec instead.
C:\Users\Ken> echo C:\Scripts\MyScript.pl | perl -MFile::Spec -lne "print $_; ($v,$d,$f)=File::Spec->splitpath($_); print $v.$d;"
C:\Scripts\MyScript.pl
C:\Scripts\
Type:
perldoc File::Spec
for more information. BTW, I tested with Strawberry perl v5.24.1

use FindBin qw( $RealBin );
say $RealBin;
Common use:
use lib $RealBin;
or
use lib "$RealBin/lib";

Using Strawberry Perl v5.26.1 I found that starting the script directly as in MyScript.pl gave a different result to starting it with Perl i.e. perl MyScript.pl. In the latter case I got the useless relative (dot) output.
The following worked for me in both cases:
use File::Basename;
use File::Spec;
my (undef, $this_script_folder, undef) = fileparse(File::Spec->rel2abs( __FILE__ ));

Related

Perl: Get (unresolved) path of symbolic link

I have a perl script whose path is /scripts/original/ascript.pl
A symbolic link to this script also exists: /scripts/linked/ascript.pl
In ascript.pl, I need the path of where the script was called from (so either /scripts/original or /scripts/linked).
abs_path() always returns the resolved location:
use strict;
use Cwd qw(abs_path);
print abs_path($0); # Always prints /scripts/original/ascript.pl
How can I get the full unresolved path?
You could use Cwd::getcwd() to get the unresolved path to script. But this has already been implemented in a more robust and general way in FindBin, so we do not have to reinvent the wheel:
use FindBin;
print '$Bin: ', $FindBin::Bin, "\n";
print '$Script: ', $FindBin::Script, "\n";
Output:
$Bin: /scripts/linked
$Script: ascript.pl
You check out the source of FindBin here.

How to pass command line arguments along with perl modules using perl?

I treid one of my previous example to pass input and output files via command line arguments using perl?
Previous example:
[Is it possible to add the file out in the File::Find::Rule using perl?
code which i tried to add input and output file as command line arguments:(generate.pl).I got struck how to include command line arguments to read and copy the files along with perl modules.
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
use Getopt::Long 'GetOptions';
use File::Path qw( make_path );
use File::Copy qw( copy move);
use File::Find::Rule qw( );
GetOptions(
'prjroot=s' => \my $input_dir,
'outdir=s' => \my $output_dir,
);
my %created;
for my $in (
File::Find::Rule
->file()
->name(qr/^[^.].*\.yfg$/)
->in($input_dir)
) {
my $match_file = substr($in, length($input_dir) + 1);
my ($match_dir) = $match_file =~ m{^(.*)/} ? $1 : '.';
my $out_dir = $output_dir . '/' . $match_dir;
my $out = $output_dir . '/' . $match_file;
make_path($out_dir) if !$created{$out_dir}++;
copy($in, $out);
}
Error occured:
./generate.pl: line 1: use: command not found
./generate.pl: line 2: use: command not found
./generate.pl: line 3: syntax error near unexpected token `('
./generate.pl: line 3: `use CGI qw(:all);'
perl execution should be as follows:
I should copy the contents from one directory to another directory along with perl modulES(I.E File::Find::Rule)
./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
Help me fix my issues .
You miss the perl comment in the first line:
#!<path_to_perl>/perl
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
.....
Your program will also work if you call it with the perl Interpreter in front of your command:
perl ./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
You've already seen what the problem is. But I'll just add that perldoc perldiag gives pretty good explanations for any error message you get from Perl. In this case, searching for "command not found" would give you this:
%s: Command not found
(A) You've accidentally run your script through csh or another shell instead of Perl. Check the #! line, or manually feed your script into Perl yourself. The #! line at the top of your file could look like
#!/usr/bin/perl -w
And that's particularly impressive, given that this error isn't actually generated by Perl - but by your shell.

How can I inspect the code of a perl module installed?

If I know that a certain perl module is installed on a system, eg. MyCompany::Database::Utils, how can I inspect the perl code of this module?
Find the source code file with whatever means your OS provides.
If you're looking for a properly installed module, you can use perldoc -l to find the file or perldoc -m to print the file (thanks #ThisSuitIsBlackNot, #mob):
perldoc -l List::Util
perldoc -m List::Util
If your module is MyCompany::Database::Utils, you know it must be in a path MyCompany/Database/Utils.pm below one of the paths in #INC.
You can list the default #INC paths with
perl -MData::Dumper -e 'print Dumper(\#INC);'
If you are on a system with mlocate (such as most Linux/BSD distros), you can also find the file with
locate MyCompany/Database/Utils.pm
If you want to look into a distribution to see the full source (e.g. to find XS code, README, unit tests etc.), you can use cpanminus' --look flag:
cpanm --look DateTime
Perl can find the module, so let Perl tell you where it found it!
perl -e'
my $p = $ARGV[0];
$p =~ s{::}{/}g;
$p .= ".pm";
require $p;
print "$INC{$p}\n";
' MyCompany::Database::Utils
If the module contains POD, you can use the following shortcut:
perldoc -l MyCompany::Database::Utils
If that doesn't find the module, it could be that the script that uses MyCompany::Database::Utils manipulates #INC to allow it to find the module. If so, add the following to your script:
END {
my $p = "MyCompany::Database::Utils";
$p =~ s{::}{/}g;
$p .= ".pm";
print "$INC{$p}\n";
}
The built-in hash %INC documented in perldoc perlvar relates each module's .pm source file to its file system location
If you have
use MyCompany::Database::Utils;
then perl will search for a file like MyCompany/Database/Utils.pm relative to any of the directories listed in array #INC and, if it is found, will put its absolute location into the %INC hash
To find where each module has been located, you can simply dump the entire hash using Data::Dump or Data::Dumper. But if you're really only interested in one module then you can examine the relevant hash element. A statement like this
print "$INC{'MyCompany/Database/Utils.pm'}\n";
will show the absolute path where that .pm file was found and loaded

Using FindBin in more than one module

Here is the scenario, I have two files:
1. dir/A.pm
2. dir/new_dir/a.t
This is how A.pm looks like:
package A;
use FindBin;
use Test::More;
is (FindBin->again, 'dir', 'got dir');
1;
This is how a.t looks like:
use FindBin;
use Test::More qw(no_plan);
use A;
is (FindBin->again, 'dir/new_dir', 'got dir/new_dir');
So I ran file a.t with perl new_dir/a.t and expect my tests to pass. But this is my test result:
not ok 1 - got dir
# Failed test 'got fir'
# at A.pm line 6.
# got: 'dir/new_dir'
# expected: 'dir'
ok 2 - got dir/new_dir
1..2
Am I doing anything wrong? I am very new to perl. Please help!!
As Dave Sherohman notes, FindBin is for finding the location of the main script, not individual modules. From the documentation:
NAME
FindBin - Locate directory of original perl script
(Admittedly, the documentation does, somewhat confusingly, refer to "modules" in the "KNOWN ISSUES" section, but it doesn't really mean what you think it means by that.)
Anyway, if you look at the source with perldoc -m FindBin, you'll see that FindBin obtains the path to the script from the $0 variable. If you're interested in finding the location of a module included via use (or require), you should look under %INC instead, something like this:
package Foo::Bar;
use File::Spec;
my ($vol, $dir, $file) = File::Spec->splitpath( $INC{'Foo/Bar.pm'} );
FindBin finds the location of the file Perl launched, not of file currently executing.
I don't see why you'd need the path to a module — File::ShareDir can be used to access your module's data files — but the following will find it:
use Cwd qw( realpath );
use File::Basename qw( dirname );
my $module_dir = dirname(realpath(__FILE__));
The same caveat as Find::Bin applies: This only works if chdir hasn't been changed.
If I understand the question correctly, a.t is in the directory dir/new_dir/ and you're using new_dir/a.t to run it from dir/, right?
If so, then it is doing the right thing. Since a.t is in dir/new_dir, you should always get dir/new_dir from FindBin - its job is to Find the Binary (program), not to find the file it's being called from, so the result will be the same in A.pm as it is in a.t.
The ->again function is for running instances of completely different programs from within the same perl interpreter, such as what mod_perl does, not for just using different modules within a single program.

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