POSIX Path resolution works through a path stepwise, from beginning to end, resolving symlinks as it goes. This is how POSIX chdir()
interprets paths, and it is consistent with the behavior of cd -P
.
cd -L
requires different behavior: that steps of the form ..
be interpreted relative to the other literal steps in the given path, regardless of whether any steps designate symbolic links. That behavior differs from chdir()
's, so you have to implement it yourself. This is largely a matter of parsing and processing appearances of .
and ..
in the path before handing off the result to chdir()
.
The POSIX specs for cd
, linked above, provide a detailed algorithm for how cd -L
should perform the needed path manipulations. As such, I have removed the algorithm I presented in an earlier version of this answer, and replaced the notes regarding it with notes pertaining to the official POSIX specifications.
Notes:
POSIX defines an obscure environment variable CDPATH
and some directory-search semantics for cd
associated with that. Details are in the linked specs. If you're going for full POSIX semantics then you'll need to handle that; otherwise, I doubt whether very many people would notice the absence of that feature.
The effect of -P
is to pass the specified path to chdir()
as-is, provided that any target path is in fact specified, or to pass the value of $HOME
to chdir()
if no path is otherwise specified.
When -L
is in effect, relative paths are interpreted with respect to the current value of $PWD
. Processing of leading ..
path steps takes that into account.
POSIX specifies that ..
processing causes cd
to fail with an error if the leading path preceding the ..
, interpreted according to the standard path-resolution rule (including symlink traversal), does not designate a directory. That is,
dir1/dir2/..
and dir1/symlink-to-dir3/..
are both ok, and both are equivalent to dir1
, but
dir1/regular-file/..
, dir1/not-a-filename/..
, and similar are erroneous.
The -L
behavior is the default if neither -P
nor -L
is given.