Why do relative paths that traverse a symlink in reverse resolve using the true path?
Clash 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.
linux bash symlink directory-structure traversal
 |Â
show 2 more comments
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.
linux bash symlink directory-structure traversal
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 usingmake
.
â 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
 |Â
show 2 more comments
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.
linux bash symlink directory-structure traversal
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.
linux bash symlink directory-structure traversal
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 usingmake
.
â 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
 |Â
show 2 more comments
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 usingmake
.
â 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
 |Â
show 2 more comments
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.
add a comment |Â
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.
add a comment |Â
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.
add a comment |Â
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.
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.
answered Jun 21 at 7:38
schily
8,57021435
8,57021435
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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