Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

who am I?

by momo33 (Beadle)
on Sep 24, 2008 at 18:53 UTC ( #713487=perlquestion: print w/ replies, xml ) Need Help??
momo33 has asked for the wisdom of the Perl Monks concerning the following question:

Dear perlmonks,

I need my perl program to find its own name and path. This must work under Linux and MS Windows. It is probable very simple, but I have no idea.

Does anyone know the trick?

Thank you.

Comment on who am I?
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.

      I have read that $0 also gives the path on some operating systems. Maybe it was in an outdated book ",

      I'm so adjective, I verb nouns!

      chomp; # nom nom nom

        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];
What script is this, and where is it? (Re: who am I?)
by ikegami (Pope) 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.

      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.

        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.

      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)).
        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.
Re: who am I?
by repellent (Priest) on Sep 24, 2008 at 23:50 UTC
    I much like:
    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.
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.

      use Cwd qw( realpath ); my $iAmHere = realpath( __FILE__ );
      Take a look at %INC, it gives you the absolute path of the loaded module.

      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

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://713487]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (8)
As of 2014-08-02 09:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (55 votes), past polls