Updated: June 9th, 2009

Problem

While writing a 1093985th Perl script the other day I was facing the following dilemma:

  • Let’s say there is a local library, called TheUberLib.pm. It is so uber that most of my scripts, located all over the machine, include it.
  • Now, let’s also say that there’s an even more uberly important binary called run_me_now_or_you_will_die but the only way to find it is by using a relative path to the aforementioned TheUberLib.pm, for example ../bin (RELATIVE TO TheUberLib.pm).
  • I don’t want to hardcode the path to run_me_now_or_you_will_die because it can be different on multiple machines and the code wouldn’t be robust enough – all I know is that the path is relative to an included library.

So how does one get the path to an included TheUberLib.pm (in my case to deduce the path to run_me_now_or_you_will_die), regardless of the script location and the current directory?

I started looking at all possible ways of getting this information.

  • At first I looked at $0. $0 returns the path to the Perl script itself (LibraryPath/lib_path.pl) and is completely useless here.
  • The FindBin module that I sometimes use was also of no help as it also deals with the caller script path.

Solution

After digging around for a bit, this is the method I came up with that did exactly what I wanted:

1
2
use File::Basename;
my $path_to_uber_lib = dirname($INC{'TheUberLib.pm'});

and the clean way to get the final path to run_me_now_or_you_will_die is:

1
2
3
my $relative_path_to_bin_dir = "../the_path_we_want/run_me_now_or_you_will_die";
my $path_to_deadly_bin = Cwd::realpath("$path_to_uber_lib/$relative_path_to_bin_dir");
print "The path to deadly bin is: $path_to_deadly_bin\n";

which prints /tmp/LibraryPath/some_path1/the_path_we_want/run_me_now_or_you_will_die.

Here is the full program with all directories that you can check out from SVN:

svn co http://beerpla.net/svn/public/Perl/LibraryPath/

or just browse the code here: http://beerpla.net/svn/public/Perl/LibraryPath/

Explanation

The %INC hash contains all included libraries, with library names as keys and full paths as values.

The dirname() function is part of File::Basename and returns just the directory part of the path.

Finally, Cwd::realpath() is a function that resolves relative and symbolic links to canonical absolute ones.

And there you have it.

● ● ●

Artem Russakovskii is a San Francisco programmer, blogger, and future millionaire (that last part is in the works). Follow Artem on Twitter (@ArtemR) or subscribe to the RSS feed.

In the meantime, if you found this article useful, feel free to buy me a cup of coffee below.



Share
  • shelly wagar

    The solution is brilliant, woud never have thought of it

  • Jamie

    This saved me lots of time, thanks :)

  • Kishore

    See whether this will helpful.
    my $mod_name = 'Archive/Zip.pm';
    foreach $dir (@INC)
    {
    my $file = "$dir/$mod_name";
    #print "nCheckiing $file";
    #`ls $file`;
    if (-f $file)
    {
    print "$mod_name is loaded from : $file";
    exit 0;
    }
    }