in reply to Perl Script own path

Update: see post Re^3: Perl Script own path with some more test data between Linux and Windows. Jim spurred some more testing and I now think that Cwd::abs_path($0) is the way to go to get full path name with the caveat that you cannot do any chdir() function before calling this.

In Perl, the automatic variable $0 in some operating systems gives the full path of the currently executing Perl script. In others you just get the name of the file that is executing. The module Cwd will give you the "current working directory". Just see attached code... executed on a Windows 32 bit XP system and a 64 bit Linux system. The combination of basename() and cwd() will always work on all OS'es.

#!/usr/bin/perl -w use strict; use File::Basename; use Cwd; print "dollar 0 is $0\n"; print basename($0), "\n"; print "current working directory is: ",cwd(),"\n"; __END__ On Windows, you get: dollar 0 is C:\TEMP\exename.pl exename.pl current working directory is: C:/TEMP On Linux, you get: dollar 0 is ./exename.pl exename.pl current working directory is: /home/xxx/yy
Moral of the story:
-basename($0) gives the name of the Perl script that is running.
-cwd() gives the current path
-concatenate the result of cwd() and basename($0) with intervening "/" to get the full path of the script in a OS independent way.

Update: Note the Windows cwd() results in "C:/TEMP"! Windows allows the use of the forward-slash ('/') in almost all situations. There is a situation where that is not true (the backslash '\' is required), but I can't remember at the moment what that is. Some Monk will probably enlighten us. My main point is that '/' almost always works to join paths on Windows.

Replies are listed 'Best First'.
Re^2: Perl Script own path
by morgon (Priest) on Jul 25, 2011 at 05:05 UTC
    On some Unix-systems (Linux among them) you can actually assign to $0 to change the way the process shows up in the process list (I've seen that done e.g. to prevent passwords that are supplied on the commandline to show up with ps).

    I now wonder if you changed your $0 to something else - would there be any way to recover the true script-path again?

      I was quite astonished! I put an $0 = 'xyzzy"; statement in my Perl code and I'll be, ps -ef shows xyzzy in the process table! I am shocked and don't know why this could happen! I would have never thought that $0 was a writable variable. My brain was thinking along the lines of C and its char** argv, which is normally used as "read only", but that's not what is happening here.

      I guess you could save: my $saved = $0; or make a { local $0; $0 = 'xyzzy'; other stuff}, but I'm not sure why that would be useful?

        My brain was thinking along the lines of C and its char** argv, which is normally used as "read only"

        argv[0] isn't read-only in C, either:

        /tmp>cat foo.c #include <string.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char ** argv) { pid_t pid; int status; strcpy(argv[0],"Did you expect this?"); pid=fork(); if (pid<0) { perror("Can't fork"); } if (pid==0) { execlp("ps","ps","-f",NULL); exit(127); } else { waitpid(pid,&status,0); } exit(0); } /tmp>make foo cc foo.c -o foo /tmp>./foo UID PID PPID C STIME TTY TIME CMD foken 1255 1254 0 15:06 pts/0 00:00:00 -bash foken 1676 1255 0 15:21 pts/0 00:00:00 Did you expect this? foken 1677 1676 0 15:21 pts/0 00:00:00 ps -f /tmp>

        Of course, manipulating argv[] is not portable. Most Unixes support it in a more or less limited way. The example from above runs on linux 2.6.29.6, and exibits the typical Linux behavior. BSDs and other Unixes may show a different behaviour. On Windows, such a program would only manipulate a copy of the original command line, generated by the C runtime library. (Windows has no concepts like fork(), exec(), or argc/argv[]. Windows programs are expected to parse a single string containing all arguments.)

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re^2: Perl Script own path
by Jim (Curate) on Jul 27, 2011 at 05:30 UTC
    concatenate the result of cwd() and basename($0) with intervening "/" to get the full path of the script in a OS independent way.

    What does the value of Cwd::cwd() have to do with the path of the Perl script? There's no general correlation.

    C:\WINDOWS>type C:\Temp\WhereAmI.pl #!perl use strict; use warnings; use Cwd; use English; use File::Basename; my $basename = File::Basename::basename($PROGRAM_NAME); my $abs_path = Cwd::abs_path($PROGRAM_NAME); my $dirname = File::Basename::dirname($abs_path); my $cwd = Cwd::cwd(); print "\$PROGRAM_NAME: $PROGRAM_NAME\n"; print "\$File::Basename::basename(\$PROGRAM_NAME): $basename\n"; print "\$Cwd::abs_path(\$PROGRAM_NAME): $abs_path\n"; print "\$File::Basename::dirname(\$abs_path): $dirname\n"; print "\$Cwd::cwd(): $cwd\n"; exit 0; C:\WINDOWS>perl \Temp\WhereAmI.pl $PROGRAM_NAME: \Temp\WhereAmI.pl $File::Basename::basename($PROGRAM_NAME): WhereAmI.pl $Cwd::abs_path($PROGRAM_NAME): C:/Temp/WhereAmI.pl $File::Basename::dirname($abs_path): C:/Temp $Cwd::cwd(): C:/WINDOWS C:\WINDOWS>
      Yes, simply Cwd::abs_path($0) appears to work the best. My previous post is not right. I did some more testing but there are a lot of cases and I suspect that there are quirky border cases out there.

      So,
      to get name of program: File::Basename::basename($0);
      to get absolute path: Cwd::abs_path($0); NOT Cwd::Cwd()
      To be portable, you must use abs_path() before doing anything that changes the current working directory, eg chdir().

      I tested on Windows XP and Fedora Linux.
      $0 on Windows was the full absolute path of the executing script, but with Windows style "\" instead of "/".
      $0 on Linux was essentially what was typed into the shell to run the script. Something like this: "../test/whereami.pl" gave exactly that for $0.

      When using Cwd::abs_path($0), on Windows the backslashes ('\') were converted to forward slashes ('/') - the full path stayed the same since $0 was the full path to begin with. A chdir() on Windows had no effect upon the result of abs_path().

      On Linux, abs_path does some text massaging of rel2abs()'s output which itself a result of using Cwd::cwd() so if working directory is changed, abs_path() will be wrong. (oh, abs_path() probably doesn't call rel2abs() but it looks like it does whatever rel2abs() does. Oh, the rel2abs() function in File::Spec... in my Unix test case it concatenates $0 onto what cwd() says. So in my test case, >cd b >../test/whereami.pl, you get a path like: blah../marsh/b/..test/whereami.pl. This is a valid path, but the detour through unrelated directory b is not needed. abs_path() evidently takes this path and eliminates the trip through dir b.

      This is also one of those cases where "use English;" just serves to further confuse the reader because:
      $program_name = File::Basename::basename($PROGRAM_NAME); because of course $0 is NOT the program's name. I don't think there is a good "english name" for $0 because "its sometimes this and sometimes that" is hard to express in one short name!

        __FILE__