What script is this, and where is it? (Re: who am I?)
by ikegami (Patriarch) on Sep 24, 2008 at 20:13 UTC
|
I avoid FindBin since the it has problems. There are easy to use alternatives:
Script's Path (Fully Qualified) | use Cwd qw( realpath );
$script_fqn = realpath($0);
|
Script's Name | use File::Basename qw( basename );
$script_fn = basename($0);
|
Script's Dir (Fully Qualified) | use Cwd qw( realpath );
use File::Basename qw( dirname );
$script_dir_fqn = dirname(realpath($0));
|
Update: I originally use File::Spec::Functions's rel2abs, but it doesn't handle symlinks properly. I've updated the table to use Cwd's realpath which does, as per replies.
| [reply] [d/l] [select] |
|
All of those mechanisms break if the script is being called by a symlink, and particularly so if nested symlinks are involved. You need to readlink($0) if (-l $0) first, and then check, and even then, it won't give you the same output as FindBin::RealBin.
Create the following directory structure in /var/tmp. Set up directories as follows (you'll need root for /usr/local/bin, so feel free to make the last step anywhere else in your path that you'd like):
cd /var/tmp
mkdir mydir
mkdir datadir
mkdir otherdir
cd otherdir
ln -s ../mydir symdir
Now take the following code and put it in /var/tmp/mydir/myname.pl:
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use File::Basename qw( dirname );
use File::Spec::Functions qw( rel2abs );
my $name = $0;
$name = readlink($name) if(-l $name);
print "Finding a data path:\n\n";
print 'dirname($name):',dirname($name),"/../datadir\n";
print 'dirname(rel2abs($name)):',dirname(rel2abs($name)),"/../datadir\
+n";
print 'dirname($0):',dirname($0),"/../datadir\n";
print 'dirname(rel2abs($0)):',dirname(rel2abs($0)),"/../datadir\n";
print '$FindBin::RealBin:',$FindBin::RealBin,"/../datadir\n";
Symlink /var/tmp/symdir/myname.pl to /usr/local/bin/myname (or somewhere else on your path, if you can't or don't want to tamper with a system directory). Run 'myname'. You get:
Finding a data path:
dirname($name):/var/tmp/otherdir/symdir/../datadir
dirname(rel2abs($name)):/var/tmp/otherdir/symdir/../datadir
dirname($0):/usr/local/bin/../datadir
dirname(rel2abs($0)):/usr/local/bin/../datadir
$FindBin::RealBin:/var/tmp/mydir/../datadir
Only FindBin correctly points you to the correct data directory location.
Frankly, even without symlink forests, FindBin is more readable, though.
| [reply] [d/l] [select] |
|
All of those mechanisms break if the script is being called by a symlink, and particularly so if nested symlinks are involved. You need to readlink($0) if (-l $0) first,
Agreed.
Only FindBin correctly points you to the correct data directory location.
That's wrong.
/var/tmp/otherdir/symdir/../datadir
is the same directory as
/var/tmp/mydir/../datadir
so you demonstrated that working with readlinked $0 works.
even then, it won't give you the same output as FindBin::RealBin.
Doesn't matter.
FindBin is more readable, though.
What good is readable if it doesn't work as well. And I don't see how dirname($script_qn) is less readable than $FindBin::RealBin.
| [reply] [d/l] [select] |
|
|
|
|
rel2abs doesn't have a problem with symlinks as No checks against the filesystem are made. ,
it simply uses Cwd::cwd() as the base (rel2abs($path,$base)).
| [reply] |
|
That it doesn't check against the file system is the problem. The goal is to find a file or directory relative to the placement of the script, not relative to the placement of a shortcut to the script.
| [reply] |
Re: who am I?
by moritz (Cardinal) on Sep 24, 2008 at 19:03 UTC
|
The name of the program is in $0, and the path can be found with FindBin. | [reply] |
|
| [reply] |
|
Yes it does include the path. I use the following to get just the base file name of the program:
use File::Basename;
my $MyName = (fileparse($0, '\..*'))[0];
| [reply] [d/l] [select] |
Re: who am I?
by repellent (Priest) on Sep 24, 2008 at 23:50 UTC
|
use Cwd qw(realpath);
use File::Basename;
my ($BIN, $BINPATH);
BEGIN { $BIN = basename($0); $BINPATH = realpath($0) }
over FindBin.
We simply take $BIN = basename($0) to ensure wrapper scripts work well. Often, $0 is symlinked to the actual executable -- we'd like to refer to the base filename as the basename of the symlink path, not the basename of the actual resolved $BINPATH. | [reply] [d/l] [select] |
Re: who am I?
by wol (Hermit) on Sep 25, 2008 at 09:43 UTC
|
I have a very closely related question - close enough to post here, I think:
How can a module determine the location of itself, independently of the CWD, and independently of the location of the script that is using it.
Eg. if the module is ~/lib/Ingenious.pm, and the script using it is ~/apps/Handy/Process.pl, and the CWD is ~/data/stuff/, then how could Ingenious.pm best determine that it's in ~/lib/ ?
After a bit of head scratching, I cobbled together a generic enough "solution":
foreach (@INC)
{
if (-f "$_/".__PACKAGE__.".pm")
{
$iAmHere = $_;
last;
}
}
This finds the location of the module either absolutely, or relative to the CWD, according to the content of @INC, which is sufficient for my purposes.
It feels like a hack, but hey - I wrote it before I found PerlMonks, so now seems like a good time to ask whether there's a better way of doing it. Any thoughts?
--
.sig : File not found.
| [reply] [d/l] |
|
use Cwd qw( realpath );
my $iAmHere = realpath( __FILE__ );
| [reply] [d/l] |
|
Take a look at %INC, it gives you the absolute path of the loaded module.
| [reply] |
|
Other posters have given the canonical answers to this already, so I won't repeat them.
Just a word of caution: if you're using modules that put objects or CODE refs in the @INC array, i.e. potentially load code without reading from a file, either method will not work. One such case are packaging modules such as PAR or ex::lib::zlib. So depending on how your module is used, the answer is: It's impossible to find its filename!
If you're going to write code that requires the location of the module file name, please think about it some more. It might not be the smartest way to solve your problem.
Cheers, Steffen
| [reply] |