chkdir() for d do ${d:+:} continue
( cd -- "$d" && d= ||
while cd -- "${d%"${d#*/}"}." && ! {
[ -n "${d##*/*}" ] && break
}; do d=${d#*/}; d=${d#"${d%%[!/]*}"}; done
pwd -P; printf "${d:+./%s\n}\n" "$d"
); done 2>/dev/null
That's a little function that should handle any path you throw at it. It will try to walk the tree if it cannot simply change directly into the directory. It breaks the loop when it should. It handles multiple arguments. For each it will either print only the whole canonical path to the directory or it will print as far as it managed to get then whatever remains of its argument's path. It skips empty arguments.
Here it is in action:
$ chkdir /tmp/chrome/some/nonexistent/path .. ../test *
/tmp/chrome
./some/nonexistent/path
/home/mikeserv
/home/mikeserv/test
/home/mikeserv/test
./file1
/home/mikeserv/test
./file2
/home/mikeserv/test
./file3
If I remove the stderr
redirect on the end there and enable debugging you can see how it works.
+ chkdir .. ///tmp/.//////chrome/not/
+ : continue
+ cd -- ..
+ d=
+ pwd -P
/home/mikeserv
+ printf \n
+ : continue
+ cd -- ///tmp/.//////chrome/not/
dash: 2: cd: can't cd to ///tmp/.//////chrome/not/
+ cd -- /.
+ [ -n ]
+ d=//tmp/.//////chrome/not/
+ d=tmp/.//////chrome/not/
+ cd -- tmp/.
+ [ -n ]
+ d=.//////chrome/not/
+ d=.//////chrome/not/
+ cd -- ./.
+ [ -n ]
+ d=/////chrome/not/
+ d=chrome/not/
+ cd -- chrome/.
+ [ -n ]
+ d=not/
+ d=not/
+ cd -- not/.
dash: 3: cd: can't cd to not/.
+ pwd -P
/tmp/chrome
+ printf ./%s\n\n not/
./not/