perlancar has asked for the wisdom of the Perl Monks concerning the following question:

I'm wondering if it's possible to get/recreate the version of working directory that is the same as the bash version given via PWD environment variable, without using the PWD (I'm thinking no). With Cwd's getcwd() and friends, symbolic links are resolved. I want absolute path of working directory but with symbolic links not resolved like the version bash gives me in PWD, which might have some symlinks in its elements.

Illustration:

% pwd /home/foo % mkdir dir1 % ln -s dir1 sym1 % cd sym1 % echo $PWD /home/foo/sym1 % perl -MCwd=getcwd -E'say getcwd' /home/foo/dir1

UPDATE: Correct the title and wording of what I am asking.

  • Comment on Getting the shell's version of working directory, without PWD's help
  • Download Code

Replies are listed 'Best First'.
Re: Getting the absolute path of a script, without PWD (update x2)
by LanX (Sage) on Jul 15, 2021 at 09:57 UTC
    > I want absolute path but with symbolic links not resolved.

    I'm not sure what exactly your asking for???

    Dependent on interpretation you might wanna try

    HTH

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    Update

    cwd() does - like documented - the same like `pwd`

    $ mkdir dir $ ln -s dir sym $ cd sym $ pwd /data/data/com.termux/files/home/sym $ perl -MCwd -E'say cwd()' /data/data/com.termux/files/home/sym ### update2: note the difference $ perl -MCwd -E'say getcwd()' /data/data/com.termux/files/home/dir
      Yup sorry, bad title. It's not the absolute path of the script I want, but the "current directory" in absolute path format. By the way, $0 and __FILE__ also do not seem to resolve symlinks so I'm guessing perl gets them from the shell also.
        You've replied to my update without reading it.

        getcwd and cwd are different and it's clearly documented

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Getting the absolute path of a script, without PWD
by hippo (Bishop) on Jul 15, 2021 at 10:24 UTC
    I'm thinking no

    I am also thinking no. Once you have performed your cd into sym1 the real directory you are in is dir1 so that's where the script thinks it is (and it's quite right to do so).

    An obvious question is why you would want this? I see no benefit.


    🦛

        The pwd command and cwd() give different results on my system (bash 4.4.20(1) on Linux).

        % cd /tmp
        % mkdir foo
        % cd foo
        % mkdir dir1
        % ln -s dir1 sym1
        % cd sym1
        
        % echo PWD
        /tmp/foo/sym1
        
        % pwd
        /tmp/foo/sym1
        
        % perl -MCwd=getcwd -E'say getcwd'
        /tmp/foo/dir1
        
        % perl -MCwd=cwd -E'say cwd'
        /tmp/foo/dir1

        But pwd and PWD sometime give different results too.

        UPDATE:The difference is pwd can be instructed to return "logical" and "physical" version, and the logical version still gets information from PWD. Cwd's documentation: "The cwd() is the most natural form for the current architecture. For most systems it is identical to `pwd` (but without the trailing line terminator)." might perhaps be improved by saying: "The cwd() is the most natural form for the current architecture. For most systems it is identical to `pwd -P` (but without the trailing line terminator).

      One reason is for logging path and the symlinked version is more meaningful and/or hides unwanted detail. For example I have a device /media/USER/1234-5678 mounted but I also keep /media/USER/MORE-MEANINGFUL-NAME symlinked to it and want to log the filenames inside the symlinked version. Okay not a great example as I should've just relabeled the removable volume in the first place, but that's the only example I can think of right now. There are other cases I've encountered previously.

        Sure - there may be some cases where you might want the symlink name rather than the target. But that's not what you've asked. Your question says "... without PWD". Why impose this arbitrary restriction? If you want what $PWD gives, why not just use it?


        🦛

        Find the symlinks and build a reverse hash lookup-table.


        Enjoy, Have FUN! H.Merijn
Re: Getting the absolute path of a script, without PWD
by ikegami (Pope) on Jul 16, 2021 at 00:29 UTC

    On Linux, the getcwd system call returns /home/foo/dir1 in your scenario.

    #include <unistd.h> #include <stdio.h> #include <limits.h> int main() { char cwd[PATH_MAX]; if (getcwd(cwd, sizeof(cwd)) == NULL) { perror("getcwd"); return 1; } printf("Current working dir: %s\n", cwd); return 0; }
    [~/sym1]$ gcc -Wall -Wextra -pedantic a.c -o a && ./a Current working dir: /home/ikegami/dir1

    It's not that bash sets the child's CWD differently than its own; /proc shows that bash's CWD is also resolved.

    [~/sym1]$ readlink /proc/$$/cwd /home/ikegami/dir1

    In fact, the system call to get the CWD returns the resolved link even if you explicitly set the CWD to the link.

    #include <unistd.h> #include <stdio.h> #include <limits.h> int main() { if (chdir("/home/ikegami/sym1")) { perror("chdir"); return 1; } char cwd[PATH_MAX]; if (getcwd(cwd, sizeof(cwd)) == NULL) { perror("getcwd"); return 1; } printf("Current working dir: %s\n", cwd); return 0; }
    [~/sym1]$ gcc -Wall -Wextra -pedantic a.c -o a && ./a Current working dir: /home/ikegami/dir1

    What this means is that in no way is the process's CWD /home/ikegami/sym1 as you think. So short of trusting $ENV{PWD} to be accurate, it's simply impossible to get /home/ikegami/sym1 from the system since that's not the process's CWD.

    use 5.010; use Path::Tiny qw( path ); say path($0)->absolute($ENV{PWD});
    [~/sym1]$ perl a.pl /home/ikegami/sym1/a.pl

    Do note that this is entirely insecure.

    A setuid script that relies on unresolved links in $0 is insecure.
    A setuid script that relies on $ENV{PWD} is insecure.

    Seeking work! You can reach me at ikegami@adaelis.com

      Thanks ikegami. Agreed. The working directory is a location chosen by the user. Due to symlinks one might reach the same location through different paths. And this state is in bash, there's no way of knowing this state unless bash exposes it.

        It does. That's what $ENV{PWD} is. But to use that relies on the parent being process being bash, and it wouldn't be safe for a setuid script to use that.

        Seeking work! You can reach me at ikegami@adaelis.com

Re: Getting the absolute path of a script, without PWD
by perlfan (Vicar) on Jul 15, 2021 at 14:33 UTC
    bash's pushd (and to list, dirs command) seems to track as a LIFO, the specified path to perceived pwd - i.e., the path actually used via series of cds (the only information that can solve this problem of state*). Cwd::Ext appears to do something similar. Unfortunately, there are no useful modules for you to leverage this behavior of pushd via bash. Maybe a useful module to create would be Bash::Pushd::Pwd or something like that.

    * See Hysteresis, Path dependence

      Cwd::Ext's abs_path_nd uses Cwd's cwd so it still resolves symlinks for retrieving "the current directory" part.
Re: Getting the absolute path of a script, without PWD
by ikegami (Pope) on Jul 15, 2021 at 23:51 UTC

    There are a number of modules that return an absolute path without resolving anything.

    use Path::Tiny qw( path ); my $abs = path($0)->absolute;

    For example, launching the script using ./a.pl or perl a.pl from /home/ikegami/foo would result in /home/ikegami/foo/a.pl.

    But note that launching the script using ../bar/a.pl or perl ../bar/a.pl from /home/ikegami/foo would result in /home/ikegami/foo/../bar/a.pl. That's because /home/ikegami/foo/../bar/a.pl can't be safely reduced to /home/ikegami/bar/a.pl because /home/ikegami/foo might be a symlink.

    Seeking work! You can reach me at ikegami@adaelis.com

      Path::Tiny's absolute() uses getcwd() so it still resolves symlinks when getting the current directory.