John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

What's the simplest way to add to @INC the directory that contains the current script?

I know I could use File::Spec::splitpath($0) to formulate it, and add it to the LIB, and could either write it as one long nested expression on the end of a use lib, or put it in a BEGIN block.

But, does $0 contain the whole path on all platforms? Is there an easier or more idiomatic way to do this?

—John

Replies are listed 'Best First'.
Re: Breaking a script into smaller files
by blakem (Monsignor) on Nov 12, 2001 at 09:33 UTC
      Perfect! That's exactly what I was looking for. Thanks.

      use FindBin; use lib $FindBin::Bin;
      —John
Re: Breaking a script into smaller files
by Flame (Deacon) on Nov 12, 2001 at 09:25 UTC
    Well, I can't say that it will work on all platforms, but you could try to push(@INC,'.')

    You can also use File::Basename on $0 to get the path and push it in similarly...


    -----BEGIN GEEK CODE BLOCK-----
    Version: 3.12
    GIT d- s:++ a--- C++++ UL P+++>++++ L+ E- W++>+++ N !o K- w+ O---- M-- V--
    PS PE Y- PGP t++(+++) 5(+++)++++ X R+@ tv+ b+++ DI+ D- G e->+++ h! r-- y-
    ------END GEEK CODE BLOCK------
    Translate

    "Weird things happen, get used to it."

    Flame ~ Lead Programmer: GMS

      I've used File::Basename for a similar purpose--I think __FILE__ is probably more appropriate than $0, though. I'm guessing that either

      use lib dirname __FILE__;
      or the push @INC variant of the same thing would work.

      I don't think $0 is appropriate because if memory serves it only contains the base name of the file if the script is invoked that way (for instance if it's in the user's PATH somewhere). Though I could be totally wrong about that :-)



      If God had meant us to fly, he would *never* have given us the railroads.
          --Michael Flanders

      ChemBoy already noted that $0 contains the name of the script, not the name of the current file.

      But he didn't point out that including "." includes the current working directory in @INC, which has no connection with the location of the file your code is in. Furthermore including "." explicitly is redundant - Perl includes "." in @INC already.

      The information about variables is covered, of course, in perlvar. The __FILE__ literal in perldata. And the knowledge of what directory "." is is covered in any introduction to filesystems.

        Well, the question itself did ask for the path to the script... not the current file. Or did I read it wrong?

        Regardles... I was just posting something that had worked for me when I needed a similar problem solved.

        If what I said was completely wrong, though, thank you for pointing it out.


        -----BEGIN GEEK CODE BLOCK-----
        Version: 3.12
        GIT d- s:++ a--- C++++ UL P+++>++++ L+ E- W++>+++ N !o K- w+ O---- M-- V--
        PS PE Y- PGP t++(+++) 5(+++)++++ X R+@ tv+ b+++ DI+ D- G e->+++ h! r-- y-
        ------END GEEK CODE BLOCK------
        Translate

        "Weird things happen, get used to it."

        Flame ~ Lead Programmer: GMS

      No, '.' is already on the @INC list. That will look in the current working directory, which is not the same as the directory containing the script.

      I agree that File::Basename::fileparse will be a little simpler in this case than File::Spec::splitpath, because it doesn't separate the volume out. So it becomes

      use lib { my ($base,$path)= File::Basename::fileparse ($0); $path };
      Assuming it's OK to leave off the @suffix parameter. The dirname function in that module looks like a natural, except for the enigmatic comment at the bottom: "For example, for the input file specification lib/, fileparse() considers the directory name to be lib/, while dirname() considers the directory name to be ." Huh? If I don't understand what it's doing, I can't use it.

      And I still wonder if $0 contains the actual path, or just what was on the command line.

      —John

Re: Breaking a script into smaller files
by Biker (Priest) on Nov 12, 2001 at 13:29 UTC

    You'll first have to decide if you want the current directory to end up in the beginning or in the end of your @INC search path. Flame already showed you how to get it in the end of @INC search path.

    I'd definitely recommend doing this in a BEGIN block. That way you're sure it will be available for all modules used by your script. If not, you may loose track of what module uses the modified search path and what module doesn't.

    The following will put the 'current directory' first (which is probably what you want in this case).

    BEGIN{unshift(@INC,'.');}


    Note. This can also be useful if you want to use a specific version of a system wide module on a box where the administrator doesn't want to upgrade to the latest and gratest of the module you're using. Or if they don't want to install that module at all. It will only make sense for no-binary modules, though.

    f--k the world!!!!
    /dev/world has reached maximal mount count, check forced.

      How does using a BEGIN block (as opposed to use lib) help you not "loose track of what module uses the modified search path and what module doesn't"?

      And isn't @INC global, so changing it first in my main script will affect all modules?

        BEGIN blocks fire at compile time, just like use statements. (Camel 3 points out that "use" is just a fancy BEGIN block. See page 465.) They also go in order, top to bottom.

        The point of changing @INC at compile time is to make sure that the new paths are there when modules are loaded. If you don't do that at compile time (with either BEGIN or use lib), it won't affect any of your modules.

        Can I say "compile time" enough?

        Update: There's no effective difference between use lib and the BEGIN block (with unshift), except that lib adds some architecture-dependent directories, if they exist. I was only addressing the "@INC is global" question. (Short answer: it is, but the important thing is *when* it is changed.)

Re: Breaking a script into smaller files
by growlf (Pilgrim) on Nov 12, 2001 at 15:53 UTC
    Well,
    $0 =~ m,(.*?)[^/\\]+$, and unshift @INC, $1;
    works pretty well.

    *G*
      Do all OS's hide the real path syntax and use a / or \ for the contents of $0? If so, what would it do with the directory :arrivals:11/01:foo.pl on a Macintosh?

      I want to always use a core module to manipulate file names, so I don't have to worry about such things.

(tye)Re: Breaking a script into smaller files
by tye (Sage) on Nov 12, 2001 at 22:23 UTC
Re: Breaking a script into smaller files
by belg4mit (Prior) on Nov 13, 2001 at 09:51 UTC
    Also note $0 is global and writeable. It seems to me that ought to be enough reason to consider not using it for this (other reasons having been pointed out).

    Besides __FILE__ is underused and much better.