184

I have Perl script and need to determine the full path and filename of the script during execution. I discovered that depending on how you call the script $0 varies and sometimes contains the fullpath+filename and sometimes just filename. Because the working directory can vary as well I can't think of a way to reliably get the fullpath+filename of the script.

Anyone got a solution?

serenesat
  • 4,611
  • 10
  • 37
  • 53
Chris Madden
  • 2,510
  • 2
  • 17
  • 11
  • I know this was a long time ago but I was just looking for a perl windows way of doing this and am quite happy with my solution #!/usr/bin/perl -w my @catalog=`dir`; $myHome = substr($catalog[3],14); $myHome = &rtrim($myHome); print qq(<$myHome>\n); # Right trim function to remove trailing whitespace sub rtrim { my $string = shift; $string =~ s/\s+$//; return $string; } just thought I'd share – user1210923 Dec 04 '20 at 17:42

23 Answers23

276

There are a few ways:

  • $0 is the currently executing script as provided by POSIX, relative to the current working directory if the script is at or below the CWD
  • Additionally, cwd(), getcwd() and abs_path() are provided by the Cwd module and tell you where the script is being run from
  • The module FindBin provides the $Bin & $RealBin variables that usually are the path to the executing script; this module also provides $Script & $RealScript that are the name of the script
  • __FILE__ is the actual file that the Perl interpreter deals with during compilation, including its full path.

I've seen the first three ($0, the Cwd module and the FindBin module) fail under mod_perl spectacularly, producing worthless output such as '.' or an empty string. In such environments, I use __FILE__ and get the path from that using the File::Basename module:

use File::Basename;
my $dirname = dirname(__FILE__);
SamB
  • 9,039
  • 5
  • 49
  • 56
Drew Stephens
  • 17,207
  • 15
  • 66
  • 82
  • 2
    This is really the best solution, especially if you already have a modified $0 – Caterham Jan 08 '12 at 01:04
  • 1
    Upvote. Not really sure why this answer was not accepted as the above accepted answer is not functional considering it involves stripping the script name off the end of the path. – vicTROLLA Mar 17 '13 at 00:27
  • 9
    It looks like abs_path needs to be used with _____FILE_____ as it may contain the name only with the path. – Aftershock Mar 23 '13 at 15:15
  • 6
    @vicTROLLA Probably because the biggest recommendation from this answer (using dirname with `__FILE__`) doesn't work quite as expected? I end up with the relative path from where the script was executed, while the accepted answer gives me the full absolute path. – Izkata Mar 26 '13 at 14:55
  • 12
    `dirname(__FILE__)` doesn't follow symlinks, so if you linked the executable file and where hoping to find the location of some other file in the install location you need to check `if( -l __FILE__)` and then `dirname(readlink(__FILE__))`. – DavidGamba Apr 18 '14 at 07:58
  • newbie here: although your solution worked (seeing as I could open files in that directory), is it normal that when I `dd $dirname` or `print $dirname` I get '.' as output? – Sos Aug 13 '14 at 17:12
  • I have a script that's being run from another script using require 'script.pl' so $0 give me the path to the calling script, not the called one. \_\_FILE\_\_ gives the right answer in this case. – kbro Mar 19 '15 at 16:44
  • How safe is it to use `File::Basename;`, meaning when this module started to be shipped with Perl core? Is it possible that some old Perl versions wouldn't have it. How to check it? – Ilia Ross Apr 05 '16 at 09:17
  • 3
    @IliaRostovtsev You can find when a module was first included in the standard modules with this incantation: `perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo`. For `File::Basename` that was Perl 5.0.0, which was released in the late '90s—I think it's save to use by now. – Drew Stephens Apr 05 '16 at 12:18
  • 1
    So here is what worked for me, putting it all together with @Aftershock (thanks) to answer the original question. `use Cwd 'abs_path'; use File::Basename; my $start; if ( -l __FILE__ ) { $start=dirname(readlink(__FILE__)); } else { $start=dirname(__FILE__); } my $fullpath = abs_path($start).'/'; my $filename= basename($start); print "$fullpath$filename";` – Grant Bowman Sep 20 '16 at 07:24
  • `dirname` is said to involve "quirks" in the Perl documentation, and it is recommended to use `fileparse`. So basically `abs_path((fileparse(__FILE__))[1])` should give the path. – David Tonhofer Oct 20 '18 at 10:45
  • I would like to add an important thing to keep in mind that is tangental to this problem. That is, people usually want to find the directory of the running script, so that they can include some modules relative to the script's path, i.e. they want to add a `use lib ( "$dirname/lib" )`. In those cases, it is important to put the `my $dirname = dirname(__FILE__);` inside a `BEGIN { ... }` block before the `use lib`, so that it will execute at compile time before that (normally `use` directives happen at compile time, so you need `BEGIN` to force the order of execution. – Georgy Vladimirov Dec 26 '19 at 01:20
  • 1
    Doesn't work: ```$ cat >testdirname use File::Basename; print dirname(__FILE__); $ perl testdirname .$ perl -v This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-gnu-thread-multi``` – user3673 Aug 23 '20 at 17:31
  • @DavidTonhofer Surely you do `abs_path` first, otherwise if the file itself is a symlink you won't be resolving that symlink, since you're only resolving the directory. That would be `(fileparse(abs_path(__FILE__)))[1]` – Lily Ballard Oct 07 '20 at 21:21
  • 1
    Worth mentioning here that `__FILE__` is _not_ always an absolute path. – Mark Reed Nov 23 '20 at 18:45
  • @MarkReed Yes, in my case it was the filename only. – Steven Fisher May 22 '21 at 17:00
150

$0 is typically the name of your program, so how about this?

use Cwd 'abs_path';
print abs_path($0);

Seems to me that this should work as abs_path knows if you are using a relative or absolute path.

Update For anyone reading this years later, you should read Drew's answer. It's much better than mine.

cxw
  • 16,685
  • 2
  • 45
  • 81
Ovid
  • 11,580
  • 9
  • 46
  • 76
  • 12
    Small comment, on activestate perl on windows $0 typically contains backslashes and abs_path returned forward slashes, so a quick "tr /\//\\/;" was needed to fix it. – Chris Madden Sep 17 '08 at 17:03
  • 3
    wanted to add that there's a `realpath`, which is a synonym to `abs_path`, incase you prefer the no-underscore name – vol7ron Oct 17 '12 at 19:02
  • @Chris , did you report bug to Cwd module maintainer? It seems like Windows adoption bug. – Znik Mar 03 '14 at 12:10
  • This reports an error "opendir(test.pl/..): is not a directory at test.pl line 8! – Scott Chu Apr 30 '14 at 06:22
  • 1
    Other problem I have: `perl -e 'use Cwd "abs_path";print abs_path($0);'` prints `/tmp/-e` – leonbloy May 15 '14 at 17:45
  • This worked fine for me under Windows 7. However, the path did not contain any links. – William Barrett Jun 23 '14 at 23:51
  • 2
    @leonbloy When you execute a script inline (with `-e`), I believe perl creates a temp file to store your inline script. Looks like the location, in your case, is `/tmp`. What did you expect the result to be? – GreenGiant Jan 14 '16 at 16:57
  • @GreenGiant, it uses the current directory in that case - but I guess @leonbloy did not expect to see `-e` appended to the path. – Richlv Aug 21 '17 at 08:02
38
use File::Spec;
File::Spec->rel2abs( __FILE__ );

http://perldoc.perl.org/File/Spec/Unix.html

rlandster
  • 7,294
  • 14
  • 58
  • 96
Mark
  • 106,305
  • 20
  • 172
  • 230
16

I think the module you're looking for is FindBin:

#!/usr/bin/perl
use FindBin;

$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";
bmdhacks
  • 15,841
  • 8
  • 34
  • 55
11

You could use FindBin, Cwd, File::Basename, or a combination of them. They're all in the base distribution of Perl IIRC.

I used Cwd in the past:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • @bmdhacks, you're right. Presumption is, you didn't change 0$. For example you do work above as soon as script starts (in initialization block), or elsewhere when you don't change $0. But $0 is excellent way to change process description visible under 'ps' unix tool :) This can show curren process status, etc. This is depended on programmer purpose :) – Znik Mar 03 '14 at 12:24
9

Getting the absolute path to $0 or __FILE__ is what you want. The only trouble is if someone did a chdir() and the $0 was relative -- then you need to get the absolute path in a BEGIN{} to prevent any surprises.

FindBin tries to go one better and grovel around in the $PATH for something matching the basename($0), but there are times when that does far-too-surprising things (specifically: when the file is "right in front of you" in the cwd.)

File::Fu has File::Fu->program_name and File::Fu->program_dir for this.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Eric Wilhelm
  • 485
  • 3
  • 2
7

Some short background:

Unfortunately the Unix API doesn't provide a running program with the full path to the executable. In fact, the program executing yours can provide whatever it wants in the field that normally tells your program what it is. There are, as all the answers point out, various heuristics for finding likely candidates. But nothing short of searching the entire filesystem will always work, and even that will fail if the executable is moved or removed.

But you don't want the Perl executable, which is what's actually running, but the script it is executing. And Perl needs to know where the script is to find it. It stores this in __FILE__, while $0 is from the Unix API. This can still be a relative path, so take Mark's suggestion and canonize it with File::Spec->rel2abs( __FILE__ );

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
wnoise
  • 9,764
  • 37
  • 47
6

In order to get the path to the directory containing my script I used a combination of answers given already.

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;

my $dir = dirname(File::Spec->rel2abs(__FILE__));
Matt
  • 61
  • 1
  • 1
6

Have you tried:

$ENV{'SCRIPT_NAME'}

or

use FindBin '$Bin';
print "The script is located in $Bin.\n";

It really depends on how it's being called and if it's CGI or being run from a normal shell, etc.

Sean
  • 4,580
  • 1
  • 19
  • 18
  • $ENV{'SCRIPT_NAME'} is empty when the script is running at console – Putnik Feb 06 '14 at 14:03
  • Bad idea because SCRIPT_NAME enviroment is depended on shell you are using. This is complete incompatibile with windows cmd.exe, and incompatibile when you call script directly from other binaries. There is no warranty this wariable is set. Above ways are much more usable. – Znik Mar 03 '14 at 12:19
2

There's no need to use external modules, with just one line you can have the file name and relative path. If you are using modules and need to apply a path relative to the script directory, the relative path is enough.

$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
daniel souza
  • 342
  • 3
  • 11
  • It does not provide the proper full path of the script if you run it like "./myscript.pl", as it would only show "." instead. But I still like this solution. – Keve Jun 27 '16 at 10:12
2

perlfaq8 answers a very similar question with using the rel2abs() function on $0. That function can be found in File::Spec.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
moritz
  • 12,710
  • 1
  • 41
  • 63
1
#!/usr/bin/perl -w
use strict;


my $path = $0;
$path =~ s/\.\///g;
if ($path =~ /\//){
  if ($path =~ /^\//){
    $path =~ /^((\/[^\/]+){1,}\/)[^\/]+$/;
    $path = $1;
    }
  else {
    $path =~ /^(([^\/]+\/){1,})[^\/]+$/;
    my $path_b = $1;
    my $path_a = `pwd`;
    chop($path_a);
    $path = $path_a."/".$path_b;
    }
  }
else{
  $path = `pwd`;
  chop($path);
  $path.="/";
  }
$path =~ s/\/\//\//g;



print "\n$path\n";

:DD

mkc
  • 11
  • 1
1

The problem with just using dirname(__FILE__) is that it doesn't follow symlinks. I had to use this for my script to follow the symlink to the actual file location.

use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
  $script_dir = dirname(readlink(__FILE__));
}
else {
  $script_dir = dirname(__FILE__);
}
DavidGamba
  • 3,503
  • 2
  • 30
  • 46
1

Are you looking for this?:

my $thisfile = $1 if $0 =~
/\\([^\\]*)$|\/([^\/]*)$/;

print "You are running $thisfile
now.\n";

The output will look like this:

You are running MyFileName.pl now.

It works on both Windows and Unix.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Yong Li
  • 11
  • 1
0

The problem with __FILE__ is that it will print the core module ".pm" path not necessarily the ".cgi" or ".pl" script path that is running. I guess it depends on what your goal is.

It seems to me that Cwd just needs to be updated for mod_perl. Here is my suggestion:

my $path;

use File::Basename;
my $file = basename($ENV{SCRIPT_NAME});

if (exists $ENV{MOD_PERL} && ($ENV{MOD_PERL_API_VERSION} < 2)) {
  if ($^O =~/Win/) {
    $path = `echo %cd%`;
    chop $path;
    $path =~ s!\\!/!g;
    $path .= $ENV{SCRIPT_NAME};
  }
  else {
    $path = `pwd`;
    $path .= "/$file";
  }
  # add support for other operating systems
}
else {
  require Cwd;
  $path = Cwd::getcwd()."/$file";
}
print $path;

Please add any suggestions.

Jonathan
  • 3,893
  • 5
  • 46
  • 77
0

Without any external modules, valid for shell, works well even with '../':

my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";

test:

$ /my/temp/Host$ perl ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ../Host/./host-mod.pl 
self=/my/temp/Host/host-mod.pl
Putnik
  • 5,925
  • 7
  • 38
  • 58
0

All the library-free solutions don't actually work for more than a few ways to write a path (think ../ or /bla/x/../bin/./x/../ etc. My solution looks like below. I have one quirk: I don't have the faintest idea why I have to run the replacements twice. If I don't, I get a spurious "./" or "../". Apart from that, it seems quite robust to me.

  my $callpath = $0;
  my $pwd = `pwd`; chomp($pwd);

  # if called relative -> add pwd in front
  if ($callpath !~ /^\//) { $callpath = $pwd."/".$callpath; }  

  # do the cleanup
  $callpath =~ s!^\./!!;                          # starts with ./ -> drop
  $callpath =~ s!/\./!/!g;                        # /./ -> /
  $callpath =~ s!/\./!/!g;                        # /./ -> /        (twice)

  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /
  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /   (twice)

  my $calldir = $callpath;
  $calldir =~ s/(.*)\/([^\/]+)/$1/;
Elmar
  • 1
0

None of the "top" answers were right for me. The problem with using FindBin '$Bin' or Cwd is that they return absolute path with all symbolic links resolved. In my case I needed the exact path with symbolic links present - the same as returns Unix command "pwd" and not "pwd -P". The following function provides the solution:

sub get_script_full_path {
    use File::Basename;
    use File::Spec;
    use Cwd qw(chdir cwd);
    my $curr_dir = cwd();
    chdir(dirname($0));
    my $dir = $ENV{PWD};
    chdir( $curr_dir);
    return File::Spec->catfile($dir, basename($0));
}
drjumper
  • 111
  • 1
  • 3
0

On Windows using dirname and abs_path together worked best for me.

use File::Basename;
use Cwd qw(abs_path);

# absolute path of the directory containing the executing script
my $abs_dirname = dirname(abs_path($0));
print "\ndirname(abs_path(\$0)) -> $abs_dirname\n";

here's why:

# this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__); 
print "dirname(__FILE__) -> $rel_dirname\n"; 

# this gives the slightly wrong answer, but in the form I want 
my $full_filepath = abs_path($0);
print "abs_path(\$0) -> $full_filepath\n";
0
use File::Basename;
use Cwd 'abs_path';
print dirname(abs_path(__FILE__)) ;

Drew's answer gave me:

'.'

$ cat >testdirname
use File::Basename;
print dirname(__FILE__);
$ perl testdirname
.$ perl -v

This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-gnu-thread-multi][1]
user3673
  • 665
  • 5
  • 21
0
use strict ; use warnings ; use Cwd 'abs_path';
    sub ResolveMyProductBaseDir { 

        # Start - Resolve the ProductBaseDir
        #resolve the run dir where this scripts is placed
        my $ScriptAbsolutPath = abs_path($0) ; 
        #debug print "\$ScriptAbsolutPath is $ScriptAbsolutPath \n" ;
        $ScriptAbsolutPath =~ m/^(.*)(\\|\/)(.*)\.([a-z]*)/; 
        $RunDir = $1 ; 
        #debug print "\$1 is $1 \n" ;
        #change the \'s to /'s if we are on Windows
        $RunDir =~s/\\/\//gi ; 
        my @DirParts = split ('/' , $RunDir) ; 
        for (my $count=0; $count < 4; $count++) {   pop @DirParts ;     }
        my $ProductBaseDir = join ( '/' , @DirParts ) ; 
        # Stop - Resolve the ProductBaseDir
        #debug print "ResolveMyProductBaseDir $ProductBaseDir is $ProductBaseDir \n" ; 
        return $ProductBaseDir ; 
    } #eof sub 
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53
  • While a source-only answer might solve the user's question, it doesn't help them understand why it works. You've given the user a fish, but instead you should teach them HOW to fish. – the Tin Man Aug 19 '13 at 21:28
-2

What's wrong with $^X ?

#!/usr/bin/env perl<br>
print "This is executed by $^X\n";

Would give you the full path to the Perl binary being used.

Evert

Eugene Loy
  • 12,224
  • 8
  • 53
  • 79
-5

On *nix, you likely have the "whereis" command, which searches your $PATH looking for a binary with a given name. If $0 doesn't contain the full path name, running whereis $scriptname and saving the result into a variable should tell you where the script is located.

foxxtrot
  • 11,214
  • 4
  • 27
  • 27