==================== Unix/Linux Pathnames ==================== -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 example /home/idallen/bin/argv.sh 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 "argv.sh". 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 the pathname starts at the ROOT. All other pathnames (no leading slash) are "relative" pathnames. These pathnames are "relative to" the current working directory. 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/.". Your current working directory is *NOT* added to absolute pathnames. Absolute pathnames are always independent of your working directory. ------------------------------------------------ Deceptive Absolute Pathnames such as ~ and ~user ------------------------------------------------ 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 a 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. /home/idallen. Try this command: echo ~root 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 usually 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 usually 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 ROOT in this case); so, /tmp/.. and / are usually equivalent. 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" does not appear to begin with a slash; but, since the shell expands "~" to be your home directory, e.g. "/home/idallen", the pathname "~/bin" is really "/home/idallen/bin", an absolute pathname. "$HOME/foo" is also an absolute pathname, since $HOME is expanded by your shell to be 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/