======================================================= Unix/Linux Pathnames (absolute, relative, dot, dot dot) ======================================================= -Ian! D. Allen - idallen@idallen.ca - www.idallen.com A Unix pathname consists of name components separated by forward slashes (not DOS backslashes!), e.g. /home/idallen/bin/argv.sh To be a valid pathname, every component to the left of the rightmost slash in a pathname must either be a directory or be a symbolic link to a directory. Only directories can have substructure that may contain other pathnames. Only a directory name can have a slash to its right. Putting a slash to the right of a non-directory results in an invalid pathame: $ wc /etc/passwd 37 89 1802 /etc/passwd $ wc /etc/passwd/ wc: /etc/passwd/: Not a directory The final component in the pathname, after the rightmost slash, can be anything - a file, directory, symbolic link, or other special file. In the file absolute pathname /home/idallen/bin/file.txt there are four directories - four components to the left of the rightmost slash. The names of those four directory components are "" (the ROOT directory, which has no name), "home", "idallen", and "bin". The final component happens to be a file name "file.txt", but the final component can be a directory or anything else. A pathname that starts with a leading slash (indicating that the path starts at the ROOT directory) is called an "absolute" pathname. Your current working directory has no effect on an absolute pathname, because an absolute pathname always starts at the ROOT and doesn't depend on your current directory. All other pathnames (no leading slash) are "relative" pathnames. These relative pathnames are "relative to" the current working directory. These relative pathnames start in the current directory, not in the ROOT. Relative pathnames act as if the current working directory were inserted in front of the pathname. For example, if your current directory is "/usr/bin" then the pathame "foo" is a relative pathname interpreted as "/usr/bin/foo". The pathname "../etc" is a relative pathname interpreted as "/usr/bin/../etc". The pathname "./hello" is interpreted as "/usr/bin/./hello". The pathname "." is interpreted as "/usr/bin/.". Example: Given this absolute pathname to the file "file.txt": /home/user/lab/foo/bar/file.txt show how to rename file.txt to be file2.txt using a relative pathname, using a starting point in every directory from "bar" up to the ROOT. Answer: The absolute file pathname /home/user/lab/foo/bar/file.txt contains six directories to the left of the final /: ROOT, home, user, lab, foo, and bar We will start in the "bar" directory and work our way up to the ROOT. 1. If the current directory is "bar", then a relative path to file.txt from "bar" is simply file.txt, e.g. $ pwd /home/user/lab/foo/bar $ mv file.txt file2.txt Go up one directory, from "bar" to its parent directory "foo": 2. If the current directory is "foo", then a relative path to file.txt from "foo" is bar/file.txt, e.g. $ cd .. $ pwd /home/user/lab/foo $ mv bar/file.txt bar/file2.txt Go up one directory, from "foo" to its parent directory "lab": 3. If the current directory is "lab", then a relative path to file.txt from "lab" is foo/bar/file.txt, e.g. $ cd .. $ pwd /home/user/lab $ mv foo/bar/file.txt foo/bar/file2.txt Go up one directory, from "lab" to its parent directory "user": 4. If the current directory is "user", then a relative path to file.txt from "user" is lab/foo/bar/file.txt, e.g. $ cd .. $ pwd /home/user $ mv lab/foo/bar/file.txt lab/foo/bar/file2.txt Go up one directory, from "user" to its parent directory "home": 5. If the current directory is "home", then a relative path to file.txt from "home" is user/lab/foo/bar/file.txt, e.g. $ cd .. $ pwd /home $ mv user/lab/foo/bar/file.txt user/lab/foo/bar/file2.txt Go up one directory, from "home" to its parent directory the ROOT: 6. If the current directory is the ROOT then a relative path to file.txt from the ROOT is home/user/lab/foo/bar/file.txt, e.g. $ cd .. $ pwd / $ mv home/user/lab/foo/bar/file.txt home/user/lab/foo/bar/file2.txt Note that in every case above, the output of "pwd" (the current directory) when prefixed to the beginning of the relative path, gives the absolute path, e.g. in #3 above, the current directory /home/user/lab prefixed to the relative pathname foo/bar/file.txt gives the absolute path /home/user/lab/foo/bar/file.txt - this is how current directories let us write shorter relative pathnames. Your current working directory is *NOT* prefixed to absolute pathnames. Absolute pathnames always start at the ROOT and are always independent of your current working directory. -------------------------------------------------------- Deceptive Absolute Pathnames such as ~, ~user, and $HOME -------------------------------------------------------- A pathname is absolute if and only if it starts with a leading slash *after all shell expansions are finished*. Most Unix shells contain shorthand ways of entering absolute pathnames that don't appear to begin with a slash. Most Unix shells will expand a leading tilde (~) on a token (command argument) to be the absolute pathname to some home directory. If the tilde stands alone or is immediately followed by a slash, the home directory substituted is the home directory found in the shell variable $HOME, which is usually set to the home directory of the user who is logged in (but can be changed). For example, "ls ~/bin" may be expanded to be "ls /home/idallen/bin". Try this command: echo ~ If the tilde is followed by a valid account name, e.g. ~idallen, the shell replaces the tilde expression with the absolute pathname of the home directory of that account, e.g. idallen has home /home/idallen. Try this command: echo ~mail ~uucp ~games ~apache Thus, "ls ~idallen/bin" uses an absolute pathname, because, ~idallen is actually expanded (by the shell) to be /home/idallen. Absolute pathnames may also hide inside shell variables, e.g. $HOME/foo is expanded by the shell to be /home/idallen/foo, an absolute pathname. A pathname is absolute if and only if it starts with a leading slash *after all shell expansions are finished*. Watch for hidden leading slashes inserted by your shell (e.g. ~, ~userid, and $HOME). -------------------------- Dot and Dot Dot - . and .. -------------------------- Every Unix directory contains the name "." (dot), which is a name that leads right back to the directory in which it is found. Every directory contains the name ".." (dot dot), which is a name that leads to the unique parent directory of the directory in which it is found. The ROOT directory is the only directory that is its own parent. ("." and ".." are the same in the ROOT directory.) For example, the pathname /tmp contains two directories: ROOT and "tmp". The pathname /tmp/. contains three directories, ROOT, "tmp", and ".". The "." is searched for in the "tmp" directory, and leads right back to "tmp"; so, /tmp and /tmp/. are equivalent. The pathname /./tmp contains three directories: ROOT, ".", and "tmp". The "." is searched for in the ROOT directory, and leads right back to ROOT; so, /./tmp and /tmp are equivalent. The pathname /./tmp/. contains four directories: ROOT, ".", "tmp", and ".". The first (leftmost) "." is searched for in the ROOT directory; the last (rightmost) "." is searched for in the "tmp" directory. Thus, /./tmp/. is equivalent to just: /tmp The pathname /tmp/.. contains three directories: ROOT, "tmp", and "..". The ".." is searched for in the "tmp" directory, and leads to the parent of the "tmp" directory (which is always ROOT, unless tmp is a symbolic link); so, /tmp/.. and / are usually equivalent. (We won't deal with symbolic links in this document. If /tmp is a symbolic link to a directory, not an actual directory, then /tmp/.. may not be the same as /.) The pathname /home/idallen/bin/../.. contains six directories: ROOT, "home", "idallen", "bin", "..", and "..". The first ".." is searched for in the "bin" directory and leads to the parent of the "bin" directory, which is "idallen". The second ".." is therefore searched for in the "idallen" directory, and leads to the parent of the "idallen" directory, which is "home". Thus, /home/idallen/bin/../.. is usually equivalent to /home. Each ".." backs up one directory level. The pathname /../tmp contains three directories: ROOT, "..", and "tmp". The ".." is searched for in the ROOT directory, and leads to the parent of the ROOT directory, which is also ROOT. ROOT is the only directory that is its own parent. Thus, /../tmp is the same as /tmp. Similarly, /../../../../../tmp is also the same as /tmp. If any of the pathname components are symbolic links to directories, not real directories, then the actions of "." and ".." are not so well behaved and the answers may differ from those given above. (We won't cover symbolic links here.) The ROOT directory can never be a symbolic link; so, /. and /.. (and /./. and /../../.., etc.) are always the same as / all by itself. ------------------------------- Relative vs. Absolute pathnames ------------------------------- A pathname is absolute if, after the shell has finished with it, the pathname starts with a slash. (If it starts with a slash, the nameless ROOT directory is the first directory in the pathname.) Otherwise, the pathname is relative to the current working directory. Note the wording "after the shell has finished with it". The pathname ~/bin (starting with a tilde character) does not appear to begin with a slash; but, since the shell expands the leading ~ (tilde) to be your home directory, e.g. /home/idallen, the pathname ~/bin is really /home/idallen/bin, an absolute pathname. Similarly, $HOME/foo is also an absolute pathname, since $HOME is a shell variable expanded by your shell to be (usually) the absolute path of your home directory. --------- Exercises --------- Given this current working directory: $ pwd /tmp/idallen and this sub-structure in the current directory: $ ls -l drwxr-xr-x 2 idallen idallen 4096 Feb 3 20:33 dir1 -rw-r--r-- 1 idallen idallen 0 Feb 3 20:33 file1 Note that I have created name "dir1" as a directory and name "file1" as a file for all the examples below, though I could have called them any names. All the relative pathnames below give the same output, because all these relative pathnames refer to the same (current) directory: $ pwd /tmp/idallen $ ls $ ls . $ ls ./. $ ls ././././././. $ ls dir1/.. $ ls ./dir1/.. $ ls dir1/../. $ ls dir1/../././././. $ ls ../idallen $ ls ./../idallen/./././. $ ls ../../tmp/idallen $ ls ../idallen/dir1/.. $ ls ../idallen/dir1/.././././. $ ls dir1/../../idallen $ ls ./dir1/../../idallen/. All the absolute pathnames below also give the same output, because all these absolute pathnames refer to the same (/tmp/idallen) directory: $ ls /tmp/idallen $ ls /tmp/idallen/././. $ ls /tmp/idallen/dir1/.. $ ls /tmp/../tmp/idallen/../idallen/dir1/.. $ ls /././tmp/./././../tmp/./././idallen/./././../././idallen/./dir1/./.. All the relative pathnames below give the same output, because all these relative pathnames refer to the same sub-directory "dir1": $ pwd /tmp/idallen $ ls dir1 $ ls dir1/. $ ls ./dir1 $ ls ./dir1/. $ ls ././././dir1/././././. $ ls ../idallen/dir1 $ ls ../idallen/dir1/../dir1/. $ ls ../../tmp/idallen/dir1/../../idallen/dir1/. All the absolute pathnames below give the same output, because all these absolute pathnames refer to the same sub-directory "/tmp/idallen/dir1": $ ls /tmp/idallen/dir1 $ ls /tmp/../tmp/idallen/dir1 $ ls /tmp/../tmp/idallen/../idallen/dir1 $ ls /tmp/../tmp/idallen/../idallen/dir1/../dir1 $ ls /././tmp/./././idallen/./././dir1/./. All the relative pathnames below give the same output, because all these relative pathnames refer to the root directory (from /tmp/idallen): $ pwd /tmp/idallen $ ls ../.. $ ls ../../../../.. $ ls dir1/../../.. $ ls dir1/../dir1/../../.. $ ls ../idallen/../.. $ ls ../../tmp/.. $ ls ../../tmp/idallen/../.. $ ls ../../tmp/idallen/../../tmp/idallen/../../././././. All the absolute pathnames below give the same output, because all these absolute pathnames refer to the root directory: $ ls / $ ls /. $ ls /./././. $ ls /tmp/.. $ ls /tmp/idallen/../.. $ ls /tmp/../tmp/idallen/../.. $ ls /tmp/idallen/dir1/../../.. These shell patterns match all non-hidden names in the /tmp/idallen directory: $ pwd /tmp/idallen $ echo * $ echo ./* $ echo dir1/../* $ echo ../idallen/* $ echo ../../tmp/idallen/* $ echo /tmp/idallen/* $ echo /tmp/idallen/dir1/../* These patterns match all non-hidden names in the /tmp directory: $ pwd /tmp/idallen $ echo ../* $ echo ./../* $ echo .././* $ echo /tmp/* $ echo /tmp/./* $ echo /././././tmp/./././* These patterns match all non-hidden names in the root directory: $ pwd /tmp/idallen $ echo ../../* $ echo ../../../../../../* $ echo /* $ echo /tmp/../* $ echo /tmp/idallen/../../* $ echo /tmp/idallen/dir1/../../../* All these commands copy the file "file1" into "file2" in the same (current) directory of "/tmp/idallen": $ pwd /tmp/idallen $ cp file1 file2 $ cp ./file1 ./file2 $ cp ././././././file1 ././././././file2 $ cp file1 ../idallen/file2 $ cp ../idallen/file1 file2 $ cp ../idallen/file1 ../idallen/file2 $ cp ../../tmp/idallen/file1 file2 $ cp file1 ../../tmp/idallen/file2 $ cp ../../tmp/idallen/file1 ../../tmp/idallen/file2 $ cp ./././././../../tmp/idallen/file1 ./././././../../tmp/idallen/file2 $ cp /tmp/idallen/file1 /tmp/idallen/file2 $ cp /tmp/../tmp/idallen/file1 /tmp/idallen/../idallen/file2 $ cp file1 /tmp/idallen/../../tmp/idallen/file2 Given directory dir1, this pathname argument is valid: $ ls dir1/. Given file name file1, this pathname argument is not valid: $ ls file1/. (A file name "file1" cannot be used as if it were a directory.) Question: What does "echo */." output? How does it differ from "echo *"? (Hint: "*" matches every name; */. only matches names where */. is valid.) Given directory dir1, this pathname argument is valid: $ ls ./dir1 Given file name file1, this pathname argument is also valid: $ ls ./file1 (Putting "./" in front of any relative pathname does nothing.) Question: What does "echo ./*" output? How does it differ from "echo *"? (Hint: "*" matches everything in the current directory; "." is a name for the current directory.) What is the difference between "echo ./*" and "echo */." ? -- | Ian! D. Allen - idallen@idallen.ca - Ottawa, Ontario, Canada | Home Page: http://idallen.com/ Contact Improv: http://contactimprov.ca/ | College professor (Free/Libre GNU+Linux) at: http://teaching.idallen.com/ | Defend digital freedom: http://eff.org/ and have fun: http://fools.ca/