Take the 2-minute tour ×

I have the following directory structure:

misha@misha-lmd:~/tmp$ ls -l
total 4.0K
-rw-r--r-- 1 misha lmd 21 Feb 18 21:00 hello.py
lrwxrwxrwx 1 misha lmd 20 Feb 18 21:01 symlink -> /home/misha/nobackup/

Next, I try the following:

misha@misha-lmd:~/tmp$ cd symlink
misha@misha-lmd:~/tmp/symlink$ cat ../hello.py 
cat: ../hello.py: No such file or directory

Why doesn't this work?

If I do this instead:

misha@misha-lmd:~/tmp/symlink$ cd ..
misha@misha-lmd:~/tmp$ cat hello.py
print "Hello World!"

Then all is well. cd handles .. properly, but cat doesn't. What is this sorcery, and how do I make things work the way I want them to?


OK, thanks to some of the answers here, I've found out a bit more about what's going on. First, cd is not actually an executable, it is a built-in command of the shell (in this case, bash):

misha@misha-lmd:~/tmp$ type cd
cd is a shell builtin

If you man bash, you can find all about the environment variables that bash uses for its housekeeping, including moving around directories. There are other built-ins, like pwd, that have counterparts that are actually executables:

misha@misha-lmd:~/tmp/symlink$ type pwd
pwd is a shell builtin
misha@misha-lmd:~/tmp/symlink$ /bin/pwd
misha@misha-lmd:~/tmp/symlink$ /bin/pwd -L

The /bin/pwd executable prints the physical path by default, but can also print the logical path given the `-L' switch. Similarly, when I try to do:

cat: ../hello.py: No such file or directory

things are failing because .. is being interpreted as the physical parent directory, not the logical one. This allows me to refine my question as:

When I specify a command-line argument to an executable, how can I get .. to mean the logical parent, not the physical one?

share|improve this question
What is /home/misha/nobackup/? Is it an existing directory? –  unwind Feb 18 '13 at 12:08
Yes, it exists. –  misha Feb 18 '13 at 12:43
add comment

3 Answers

up vote 4 down vote accepted

Because the directory .. in your symlinked directory is your home directory.

../something means "go to the .. directory", not "strip the last path component".

You can try pwd -P to see where you are, after you change into symlink.

share|improve this answer
If, from within the symlinked directory, .. is ~, why does "cd .." take me back to ~/tmp instead of the ~? Is cd doing something clever that cat doesn't do? –  misha Feb 18 '13 at 12:20
Yes, bash does "something clever". Just try man bash and look for cd (/^ +cd should get you there). –  Michael Krelin - hacker Feb 18 '13 at 14:04
add comment

It doesn't work because there's no /home/misha/hello.py. A symbolic link does not create a new directory, but points to the one linked. So when you cd to a symbolic link, you actually cd to that directory,

Did you expect the shell will remember from where you came from a symbolic link? Well, doesn't work like that :)

share|improve this answer
Umm... Well, the shell actually does remember, as you cas easily see in prompt ;-) –  Michael Krelin - hacker Feb 18 '13 at 12:12
The question was caused by cd command actually working like that (unless it's cd -P), as opposed to .. pathname interpretation in system calls. –  Anton Kovalenko Feb 18 '13 at 12:13
add comment


cat $(cd ..; pwd)/hello.pycat

Don't know if it helps you.

If you really need it, here is a way to do it (but it's kind of ugly):

$ lcat ( ) ( cd "${1%/*}"; cat "${1##*/}"; )
# use it like 'cat'
$ lcat ../hello.py

Using ( ... ) instead of { ... } for the function body makes it do the cd in a sub shell instead of the current shell.
When I try it it works also with "file name completion".
But this is only solving cat, it can be more complex to use for other commands as well I think.

$ lcmd ( ) ( local cmd="$1"; shift; cd "${1%/*}"; $cmd "${1##*/}"; )

Then do e.g. lcmd cat ../hello.py

share|improve this answer
Thank you. While that works, it's really verbose, and it breaks autocompletion. –  misha Feb 18 '13 at 14:01
@misha: I updated with some kind of "hack". –  244an Feb 19 '13 at 2:17
add comment

Your Answer


