Why do relative paths that traverse a symlink in reverse resolve using the true path?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
-1
down vote

favorite












Either I've completely misunderstood something about symlinks (most likely), or their behaviour has changed at some point and I'm now catching up.



I have a script directory that resides somewhere on my file system, let's say /tmp/scripts for the sake of this explanation. This script directory is intended be added to an arbitrary project by creating a symlink to it, within the project - e.g. ln -s /tmp/scripts $(PROJECT)/scripts. The scripts within this directory expect to find some project-specific info in ../config (i.e. a subdirectory of the project). However, what I'm finding is that when scripts are run from within the script directory they look in /tmp/config (which doesn't exist) rather than $(PROJECT)/config, resolving the relative path against the true location of the script directory, and ignoring the symlink context. This is true even though cd $(PROJECT)/scripts; pwd shows the symlink context.



Here's a simple example of this, on Ubuntu 16.04, with Bash 4.3.48:



$ mkdir a
$ touch a/apples
$ mkdir -p b/a
$ touch b/a/bananas
$ mkdir -p b/c
$ ln -s b/c c
$ cd c
$ pwd
/home/user/c # note this does not show '/home/user/b/c'
$ ls ../a
bananas
$ cd ../a
$ ls
apples


This surprises me, because it suggests that a process with a working directory behind a symlink does not have the same environment as the process that invoked it.



Perhaps it's always been like this? Why does the script process know that it's not really in $(PROJECT)/scripts yet pwd does not? Is pwd faking it? Can anyone shed some light please?



EDIT: here's a simple practical example, that first sets up the relative directories, then creates a script (in this case a simple Makefile) and then invokes it:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/a
echo "A:=77" > b/a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


This will output 77 whereas I had hoped it that it might output 42, as that would be the value set by the project for use by the symlinked Makefile et al. In reality, the directory b/a doesn't actually exist, so instead:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


Resulting in:



$ ./setup
Makefile:1: ../a/Config: No such file or directory
make: *** No rule to make target '../a/Config'. Stop.






share|improve this question





















  • cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
    – Michael Homer
    Jun 21 at 0:06










  • Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
    – Michael Homer
    Jun 21 at 0:07










  • A related question is of course unix.stackexchange.com/questions/413204 .
    – JdeBP
    Jun 21 at 0:22










  • @MichaelHomer good idea - I've added an example that shows this behaviour using make.
    – meowsqueak
    Jun 21 at 2:06










  • @JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
    – meowsqueak
    Jun 21 at 2:07














up vote
-1
down vote

favorite












Either I've completely misunderstood something about symlinks (most likely), or their behaviour has changed at some point and I'm now catching up.



I have a script directory that resides somewhere on my file system, let's say /tmp/scripts for the sake of this explanation. This script directory is intended be added to an arbitrary project by creating a symlink to it, within the project - e.g. ln -s /tmp/scripts $(PROJECT)/scripts. The scripts within this directory expect to find some project-specific info in ../config (i.e. a subdirectory of the project). However, what I'm finding is that when scripts are run from within the script directory they look in /tmp/config (which doesn't exist) rather than $(PROJECT)/config, resolving the relative path against the true location of the script directory, and ignoring the symlink context. This is true even though cd $(PROJECT)/scripts; pwd shows the symlink context.



Here's a simple example of this, on Ubuntu 16.04, with Bash 4.3.48:



$ mkdir a
$ touch a/apples
$ mkdir -p b/a
$ touch b/a/bananas
$ mkdir -p b/c
$ ln -s b/c c
$ cd c
$ pwd
/home/user/c # note this does not show '/home/user/b/c'
$ ls ../a
bananas
$ cd ../a
$ ls
apples


This surprises me, because it suggests that a process with a working directory behind a symlink does not have the same environment as the process that invoked it.



Perhaps it's always been like this? Why does the script process know that it's not really in $(PROJECT)/scripts yet pwd does not? Is pwd faking it? Can anyone shed some light please?



EDIT: here's a simple practical example, that first sets up the relative directories, then creates a script (in this case a simple Makefile) and then invokes it:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/a
echo "A:=77" > b/a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


This will output 77 whereas I had hoped it that it might output 42, as that would be the value set by the project for use by the symlinked Makefile et al. In reality, the directory b/a doesn't actually exist, so instead:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


Resulting in:



$ ./setup
Makefile:1: ../a/Config: No such file or directory
make: *** No rule to make target '../a/Config'. Stop.






share|improve this question





















  • cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
    – Michael Homer
    Jun 21 at 0:06










  • Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
    – Michael Homer
    Jun 21 at 0:07










  • A related question is of course unix.stackexchange.com/questions/413204 .
    – JdeBP
    Jun 21 at 0:22










  • @MichaelHomer good idea - I've added an example that shows this behaviour using make.
    – meowsqueak
    Jun 21 at 2:06










  • @JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
    – meowsqueak
    Jun 21 at 2:07












up vote
-1
down vote

favorite









up vote
-1
down vote

favorite











Either I've completely misunderstood something about symlinks (most likely), or their behaviour has changed at some point and I'm now catching up.



I have a script directory that resides somewhere on my file system, let's say /tmp/scripts for the sake of this explanation. This script directory is intended be added to an arbitrary project by creating a symlink to it, within the project - e.g. ln -s /tmp/scripts $(PROJECT)/scripts. The scripts within this directory expect to find some project-specific info in ../config (i.e. a subdirectory of the project). However, what I'm finding is that when scripts are run from within the script directory they look in /tmp/config (which doesn't exist) rather than $(PROJECT)/config, resolving the relative path against the true location of the script directory, and ignoring the symlink context. This is true even though cd $(PROJECT)/scripts; pwd shows the symlink context.



Here's a simple example of this, on Ubuntu 16.04, with Bash 4.3.48:



$ mkdir a
$ touch a/apples
$ mkdir -p b/a
$ touch b/a/bananas
$ mkdir -p b/c
$ ln -s b/c c
$ cd c
$ pwd
/home/user/c # note this does not show '/home/user/b/c'
$ ls ../a
bananas
$ cd ../a
$ ls
apples


This surprises me, because it suggests that a process with a working directory behind a symlink does not have the same environment as the process that invoked it.



Perhaps it's always been like this? Why does the script process know that it's not really in $(PROJECT)/scripts yet pwd does not? Is pwd faking it? Can anyone shed some light please?



EDIT: here's a simple practical example, that first sets up the relative directories, then creates a script (in this case a simple Makefile) and then invokes it:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/a
echo "A:=77" > b/a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


This will output 77 whereas I had hoped it that it might output 42, as that would be the value set by the project for use by the symlinked Makefile et al. In reality, the directory b/a doesn't actually exist, so instead:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


Resulting in:



$ ./setup
Makefile:1: ../a/Config: No such file or directory
make: *** No rule to make target '../a/Config'. Stop.






share|improve this question













Either I've completely misunderstood something about symlinks (most likely), or their behaviour has changed at some point and I'm now catching up.



I have a script directory that resides somewhere on my file system, let's say /tmp/scripts for the sake of this explanation. This script directory is intended be added to an arbitrary project by creating a symlink to it, within the project - e.g. ln -s /tmp/scripts $(PROJECT)/scripts. The scripts within this directory expect to find some project-specific info in ../config (i.e. a subdirectory of the project). However, what I'm finding is that when scripts are run from within the script directory they look in /tmp/config (which doesn't exist) rather than $(PROJECT)/config, resolving the relative path against the true location of the script directory, and ignoring the symlink context. This is true even though cd $(PROJECT)/scripts; pwd shows the symlink context.



Here's a simple example of this, on Ubuntu 16.04, with Bash 4.3.48:



$ mkdir a
$ touch a/apples
$ mkdir -p b/a
$ touch b/a/bananas
$ mkdir -p b/c
$ ln -s b/c c
$ cd c
$ pwd
/home/user/c # note this does not show '/home/user/b/c'
$ ls ../a
bananas
$ cd ../a
$ ls
apples


This surprises me, because it suggests that a process with a working directory behind a symlink does not have the same environment as the process that invoked it.



Perhaps it's always been like this? Why does the script process know that it's not really in $(PROJECT)/scripts yet pwd does not? Is pwd faking it? Can anyone shed some light please?



EDIT: here's a simple practical example, that first sets up the relative directories, then creates a script (in this case a simple Makefile) and then invokes it:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/a
echo "A:=77" > b/a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


This will output 77 whereas I had hoped it that it might output 42, as that would be the value set by the project for use by the symlinked Makefile et al. In reality, the directory b/a doesn't actually exist, so instead:



#!/bin/bash
mkdir -p a
echo "A:=42" > a/Config
mkdir -p b/c
ln -sfn b/c c
echo -e "include ../a/Confignall:nt@echo $A" > c/Makefile
cd c
make


Resulting in:



$ ./setup
Makefile:1: ../a/Config: No such file or directory
make: *** No rule to make target '../a/Config'. Stop.








share|improve this question












share|improve this question




share|improve this question








edited Jun 21 at 2:05
























asked Jun 20 at 23:51









meowsqueak

992




992











  • cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
    – Michael Homer
    Jun 21 at 0:06










  • Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
    – Michael Homer
    Jun 21 at 0:07










  • A related question is of course unix.stackexchange.com/questions/413204 .
    – JdeBP
    Jun 21 at 0:22










  • @MichaelHomer good idea - I've added an example that shows this behaviour using make.
    – meowsqueak
    Jun 21 at 2:06










  • @JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
    – meowsqueak
    Jun 21 at 2:07
















  • cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
    – Michael Homer
    Jun 21 at 0:06










  • Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
    – Michael Homer
    Jun 21 at 0:07










  • A related question is of course unix.stackexchange.com/questions/413204 .
    – JdeBP
    Jun 21 at 0:22










  • @MichaelHomer good idea - I've added an example that shows this behaviour using make.
    – meowsqueak
    Jun 21 at 2:06










  • @JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
    – meowsqueak
    Jun 21 at 2:07















cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
– Michael Homer
Jun 21 at 0:06




cd is evaluated by your shell, it's not a good test case. ls is reading the actual .. entry.
– Michael Homer
Jun 21 at 0:06












Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
– Michael Homer
Jun 21 at 0:07




Could you give an example of an actual script that exhibits the behaviour you're asking about? Show what happens, and what you expect/what you think used to happen, and if there are any cases that do behave as expected (which it sounds like there are) include those too.
– Michael Homer
Jun 21 at 0:07












A related question is of course unix.stackexchange.com/questions/413204 .
– JdeBP
Jun 21 at 0:22




A related question is of course unix.stackexchange.com/questions/413204 .
– JdeBP
Jun 21 at 0:22












@MichaelHomer good idea - I've added an example that shows this behaviour using make.
– meowsqueak
Jun 21 at 2:06




@MichaelHomer good idea - I've added an example that shows this behaviour using make.
– meowsqueak
Jun 21 at 2:06












@JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
– meowsqueak
Jun 21 at 2:07




@JdeBP thanks for the link, that's led to some good explanations and helped demystify things a bit. Now I'm wondering how to make all my scripts consistent to use either the Logical path all the time, or the Physical path all the time.
– meowsqueak
Jun 21 at 2:07










1 Answer
1






active

oldest

votes

















up vote
-1
down vote













If you like cd to behave as you expect, call:



cd -P c


and pwd will print:



/home/user/b/c


What you see is a problem that is caused by the fact that your shell follows an unhappy POSIX decision and makes cd -L the default. This causes the shell to remember the apparent $PWD /home/user/c. Since the shell command cd .. does not call chdir("..") but rather shortens the $PWD value by one element and then chdir()s to that shortened path, cd .. gets you back from where you did come.



This is a security risk, as people tend to run e.g.:



ls ../


and after being happy with the result, then may run:



cd ../; rm *.c


and remove different files than expected.



Note that the POSIX default is modeled after the default behavior of ksh88 and at the same time when ksh introduced that default behavior, AT&T decided to implement the safer variant cd -P as the default in the Bourne Shell.






share|improve this answer





















    Your Answer







    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "106"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f450994%2fwhy-do-relative-paths-that-traverse-a-symlink-in-reverse-resolve-using-the-true%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    -1
    down vote













    If you like cd to behave as you expect, call:



    cd -P c


    and pwd will print:



    /home/user/b/c


    What you see is a problem that is caused by the fact that your shell follows an unhappy POSIX decision and makes cd -L the default. This causes the shell to remember the apparent $PWD /home/user/c. Since the shell command cd .. does not call chdir("..") but rather shortens the $PWD value by one element and then chdir()s to that shortened path, cd .. gets you back from where you did come.



    This is a security risk, as people tend to run e.g.:



    ls ../


    and after being happy with the result, then may run:



    cd ../; rm *.c


    and remove different files than expected.



    Note that the POSIX default is modeled after the default behavior of ksh88 and at the same time when ksh introduced that default behavior, AT&T decided to implement the safer variant cd -P as the default in the Bourne Shell.






    share|improve this answer

























      up vote
      -1
      down vote













      If you like cd to behave as you expect, call:



      cd -P c


      and pwd will print:



      /home/user/b/c


      What you see is a problem that is caused by the fact that your shell follows an unhappy POSIX decision and makes cd -L the default. This causes the shell to remember the apparent $PWD /home/user/c. Since the shell command cd .. does not call chdir("..") but rather shortens the $PWD value by one element and then chdir()s to that shortened path, cd .. gets you back from where you did come.



      This is a security risk, as people tend to run e.g.:



      ls ../


      and after being happy with the result, then may run:



      cd ../; rm *.c


      and remove different files than expected.



      Note that the POSIX default is modeled after the default behavior of ksh88 and at the same time when ksh introduced that default behavior, AT&T decided to implement the safer variant cd -P as the default in the Bourne Shell.






      share|improve this answer























        up vote
        -1
        down vote










        up vote
        -1
        down vote









        If you like cd to behave as you expect, call:



        cd -P c


        and pwd will print:



        /home/user/b/c


        What you see is a problem that is caused by the fact that your shell follows an unhappy POSIX decision and makes cd -L the default. This causes the shell to remember the apparent $PWD /home/user/c. Since the shell command cd .. does not call chdir("..") but rather shortens the $PWD value by one element and then chdir()s to that shortened path, cd .. gets you back from where you did come.



        This is a security risk, as people tend to run e.g.:



        ls ../


        and after being happy with the result, then may run:



        cd ../; rm *.c


        and remove different files than expected.



        Note that the POSIX default is modeled after the default behavior of ksh88 and at the same time when ksh introduced that default behavior, AT&T decided to implement the safer variant cd -P as the default in the Bourne Shell.






        share|improve this answer













        If you like cd to behave as you expect, call:



        cd -P c


        and pwd will print:



        /home/user/b/c


        What you see is a problem that is caused by the fact that your shell follows an unhappy POSIX decision and makes cd -L the default. This causes the shell to remember the apparent $PWD /home/user/c. Since the shell command cd .. does not call chdir("..") but rather shortens the $PWD value by one element and then chdir()s to that shortened path, cd .. gets you back from where you did come.



        This is a security risk, as people tend to run e.g.:



        ls ../


        and after being happy with the result, then may run:



        cd ../; rm *.c


        and remove different files than expected.



        Note that the POSIX default is modeled after the default behavior of ksh88 and at the same time when ksh introduced that default behavior, AT&T decided to implement the safer variant cd -P as the default in the Bourne Shell.







        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered Jun 21 at 7:38









        schily

        8,57021435




        8,57021435






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f450994%2fwhy-do-relative-paths-that-traverse-a-symlink-in-reverse-resolve-using-the-true%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            How to check contact read email or not when send email to Individual?

            Bahrain

            Postfix configuration issue with fips on centos 7; mailgun relay