Reputation: 3073
This is a subtle question, I know, but I hope you can bear with me for a moment.
Suppose /tmp/dir
is a symlink to /home/user/some/dir
. Suppose also that your current working directory is /tmp/dir
.
Even expanding something like .
does not seem to be possible, as os.getcwd()
returns /home/user/some/dir
instead of /tmp/dir
, which is what pwd
command returns. Relative dir can also be ../dir/../dir/subdir
, .././././dir/foo
, etc.
So my question: Is there any reliable function that does path expansion of a relative path but does not follow the symlink that may exist in the relative path. In case of ../dir/../dir/subdir
, for example, I would like to get /tmp/dir/subdir
and NOT /home/user/some/dir/subdir
.
Just to avoid getting something I do not want, the answer is NOT os.path.abspath
, os.path.realpath
, os.path.expanduser
, or os.path.relpath
.
Upvotes: 0
Views: 1188
Reputation: 42030
Seems as if you're not the first to notice this odd behavior of chdir(2)
.
There's nothing about it in the Linux manpage, but a similar page says...
int chdir(const char *path);
[...]
The chdir() function makes the directory named by path the new current directory. If the last component of path is a symbolic link, chdir() resolves the contents of the symbolic link. If the chdir() function fails, the current directory is unchanged.
...although with no explanation as to why it resolves the symbolic link.
So, you can't technically have a current working directory of /tmp/dir
, even if your shell claims otherwise.
However, you can exploit the fact that the shell's built-in cd
command sets the environment variable PWD
to the value you entered, so you can do this...
$ cd /tmp/dir
$ python
>>> import os
>>> os.getcwd()
'/home/user/some/dir'
>>> os.environ['PWD']
'/tmp/dir'
>>> os.path.normpath(os.path.join(os.environ['PWD'], '../dir/../dir/subdir'))
'/tmp/dir/subdir'
...although it may fail in cases when the process wasn't started from a shell.
Upvotes: 1
Reputation: 10971
Not sure that's what you're looking for, but... you can write a function that generate the "logical" full path from the relative path and current working directory then check that the generated path really exists on the system. That function could look like:
import os
def absolute_path(path):
wd = os.getcwd()
full = os.path.join(wd, path)
parts = full.split(os.sep)
kept = []
skip = 0
for part in reversed(parts):
if part == '..':
skip += 1
elif skip == 0:
kept.insert(0, part)
else:
skip -= 1
return os.sep + os.path.join(*kept)
Upvotes: 0