in reply to Re^2: What script is this, and where is it? (Re: who am I?)
in thread who am I?

Edit: claims withdrawn. See below. Original message preserved below the jump.

*cough* ... look again.

'/var/tmp/otherdir/symdir/../datadir' => '/var/tmp/otherdir/datadir' '/var/tmp/mydir/../datadir' => '/var/tmp/datadir'

You'll only ever get the same location if you are on the same directory level as the directory you symlink to. I suppose I should have made it easier to see by changing out /var/tmp/otherdir with just /tmp or some such.

Edit:

Here's the result if you make the last symlink just into /tmp:

Finding a data path: dirname($name):/tmp/../datadir dirname(rel2abs($name)):/tmp/../datadir dirname($0):/usr/local/bin/../datadir dirname(rel2abs($0)):/usr/local/bin/../datadir $FindBin::RealBin:/var/tmp/mydir/../datadir
Does that make it clearer for you?

Edit 2:

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.

Specifically, I find:

dirname(rel2abs($0)) . "/../datadir"
less readable than
$FindBin::RealBin . "/../datadir"
because of the nested parens.

Replies are listed 'Best First'.
Re^4: What script is this, and where is it? (Re: who am I?)
by ikegami (Patriarch) on Sep 24, 2008 at 23:10 UTC

    *cough* ... look again.

    /var/tmp/otherdir/symdir/../datadir' => '/var/tmp/otherdir/datadir'

    I read it fine the first time.

    /var/tmp/otherdir/symdir/../datadir

    is equivalent to

    /var/tmp/datadir

    and not equivalent to

    /var/tmp/otherdir/datadir

    In "/var/tmp/otherdir/symdir/../datadir", the ".." is that of "mydir" which is a hard link to "/var/tmp".

    Do like I did and test before contradicting someone.

    $ cd /var/tmp $ mkdir mydir $ mkdir datadir $ mkdir otherdir $ touch mydir/in_mydir $ touch datadir/in_datadir $ touch otherdir/in_otherdir $ cd otherdir $ ln -s ../mydir symdir $ ls /var/tmp/otherdir/symdir/../datadir in_datadir $ perl -le'opendir $dh, $ARGV[0] or die; print for grep /in/, readdir +$dh' /var/tmp/otherdir/symdir/../datadir in_datadir
      Ok, so, soon on CPAN (when I have time to write the PODs):
      package File::ScriptName; use strict; use warnings; use version; our $VERSION = qv('0.1'); our $AUTHORITY = 'MASSA'; use Exporter q(import); our @EXPORT_OK = qw(myname mybase mydir); use Carp; use Cwd qw(realpath); use File::Spec::Functions qw(rel2abs); use File::Basename qw(basename dirname); BEGIN { my $myname = $0 = realpath rel2abs $0; my $mybase = basename $0, qw(.t .pm .pl .perl); my $mydir = dirname $0; croak 'chdir() used too early' unless -f $0; sub myname { $myname } sub mybase { $mybase } sub mydir { $mydir } } 1
      Just use $0 (or File::ScriptName::myname) to find the real script name or use File::ScriptName::mybase and File::ScriptName::mydir for whatever purposes you want; traverses all symlinks and stuff... is it enough??
      Update: croaks if you chdir before executing the module (possibly in a BEGIN block). Don't do that.

      UpUpdate:It's on CPAN now, as scriptname...
      []s, HTH, Massa (κς,πμ,πλ)
        Awesome!

        rel2abs isn't needed.
        realpath rel2abs $0
        is the same as
        realpath $0

        Is there a reason to croak anymore, now that realpath is used instead of rel2abs?

        I just tried this code with an executable bound with PerlApp. The code as written works fine if the executable is called by "exename.exe". However, it dies if the executable is called by "exename".

        Here's my ugly modification to your code. There's probably a nicer way to do it. Clean it up as you desire.

        BEGIN { my $rel2abs = rel2abs $0; my $realpath = eval { realpath $rel2abs }; unless ( defined $realpath ) { warn "$rel2abs"; $rel2abs .= '.exe'; $realpath = realpath $rel2abs; } my $myname = $0 = $realpath; #my $myname = $0 = realpath rel2abs $0; my $mybase = basename $0, qw(.t .pm .pl .perl .exe); my $mydir = dirname $0; die 'chdir() used too early' unless -f $0; sub myname { $myname } sub mybase { $mybase } sub mydir { $mydir } }

        Thanks for the useful code. ++!.


        TGI says moo

      Do like I did and test before contradicting someone.

      Huh, okay, that's unexpected, probably because the last time I did this I was dealing with a symlink to a symlink, not a symlink to a real file just in a symlinked directory. I should have used the /tmp example to begin with to avoid confusion.

      Still, my main point holds: dirname(rel2abs($0)) breaks under symlinks, and even a single readlink won't get you out of nested symlinks.

      Realpath works, and dirname(realpath($0)) is consistent, accurate, and clean. I withdraw all prior claims, and will be using this in the future. I don't see the need for a module for it, though I give my thanks to massa for the effort.

      My original test code on rel2abs (with the major bug removed) remains after the jump for the curious.

        You are considering that datadir is a sibling to the symlink's directory. I, OTOH, consider that datadir will be most probably a sibling to the original script file's directory (because you can symlink it from anywhere, and considering otherwise would be a security risk *). We will have to agree on disagreeing.
        * create symlink, create malicious configuration file side-by-side with symlink, ... profit!
        Update: I stand corrected about the (meaning of) final destination of the datadir according to the answer by AZed below... You're welcome! And seems that we will have to agree in agreeing :-)
        []s, HTH, Massa (κς,πμ,πλ)
        The fix is to use Cwd::realpath instead of rel2abs. I'd appreciate it if you could test that for me.