0

I'm a beginner to learn system program.

I googled for the uses of cd .., cd ., and cd ..., but it turns out that what I only know is that cd .. means to move back one dir.

I tried them in my terminal on MacOS. Typing cd .. works. However, when I typed cd ..., it said that

zsh: command not found: cd...

While typing cd ., the path doesn't change.

Plz help me for this question, thanks.

AdminBee
  • 22,803

2 Answers2

2

The directories . and .. are special and reference the current directory and its parent directory respectively.

The command cd changes directories. So cd . does nothing special (you go to where you are), etc.

All unix shells require commands and their arguments to separated by whitespace, so cd... is not the cd command, and doesn't exist. On some systems, ... is a shortcut for two directories up, but this is not standard and doesn't work on any current systems I am aware of. So cd... likely will give command not found and cd ... will likely give no such file or directory.

The cd command is a shell builtin and documentation for it can be found in your shell's man page. For example, man bash or man bash-builtins

Details of how path resolution works (including the use of . and .. and / can be found in man path_resolution and in the POSIX standard at https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13

user10489
  • 6,740
0

In system calls that take file paths as arguments, . is a relative path that refers to here, and .. to the parent directory. some/dir/. is the same as some/dir¹, some/dir/.. is the parent directory of some/dir, possibly some if some/dir is not a symlink.

Traditionally, . and .. were implemented as hard links, . to the directory it's in, .. to its parent, initially by the mkdir utility and later by the mkdir() system call. Depending on the filesystem, it may or may not still be the case. Those . and .. may or may not be returned by readdir() / getdents(), but regardless, they are always required to resolve to here and the parent directory in system calls that take file paths as arguments.

As a consequence, that applies as well to most commands that take file paths as arguments:

  • vi some/dir/../file edits the file file in the parent directory of some/dir (not necessarily the same as some/file).
  • ls is short for ls . which lists the contents of the current working directory. ls -ld short for ls -ld . which lists details of the current working directory file.
  • ls -l .. lists the contents of the parent directory of the current working directory.

cd (and pushd in those shells that have it) is an exception in most shells though, as cd implements its own logical directory traversal.

cd some/dir/.. doesn't do a chdir("some/dir/..") (where chdir() is the actual system call to change the current working directory), but chdir("some") or in some shells chdir("$PWD/some") (sometimes, even if $PWD is no longer a path to the current working directory). And cd .. is not doing chdir("..") but something like chdir(dirname($PWD)).

That is, cd (and cd only) treats .. specially by itself in order to treat symlinks as if they were directories, in order to try and minimise the risk of surprises to the user. For instance, cd some/dir; cd ../.. should get you back to where you were initially even if some/dir or some happens to be a symlink to /somewhere/entirely/different.

It's important to bear in mind as it can cause some confusion. vi some/dir/../file may edit a different file from cd some/dir/.. && vi file for instance.

For cd to treat .. the same as other commands, you need the -P option². And that's why it's often recommended to use cd -P in scripts (so a program written in shell works the same as one written in any other language in that regard).

Now, ... is not special, so it's treated as any other file.

mkdir ... && cd ... will create a directory called ... (hidden as it starts with a .) and chdir() into it (as chdir("...") or chdir("$PWD/...") depending on the shell).

cd ... would return an error that ... doesn't exists if it doesn't like cd any/other/inexistent/directory.

cd... without whitespace between cd and ... would try to call the cd... command, which likely doesn't exist in your environment and return an error accordingly.

Now, to shorten typing, some people create an alias for cd... as:

alias cd...='cd ../..'  cd....='cd ../../..'

So calling cd... would cd to the parent directory of the parent directory of the current working directory (logically, as in it would take two directory components off the tail of $PWD, not do a chdir("../..") as seen above).

zsh also has global aliases which are expanded anywhere, not only in command position like regular aliases, so some people have been known to do:

alias -g ...=../.. ....=../../..

Which means that cd ... and ls ... get changed to cd ../.. or ls ../.. before being executed. That doesn't apply to cd .../dir though. There are better ways to achieve something similar using custom bindings to the . key.


¹ though if some/dir is a symlink, some/dir/., like some/dir/ would refer to the directory the symlink points to, a distinction that matters for system calls that apply prior to symlink resolution such as lstat(), lchown(), readlink(), unlink() and not after (like open(), chown()...).

² well, -P doesn't prevent the $CDPATH/$cdpath handling nor the special treatment of - (or -1, +2... in zsh), but that's a separate issue.