Navigating the Unix Filesystem

A Unix filesystem is one connected tree of names. Learning to move around that tree, and to state exactly which name a command will act on, is more useful than memorising a long list of commands. This article uses POSIX shell ideas first. GNU/Linux, the BSDs, and macOS share these basics, but options and default utilities can differ; consult man command on the machine you are using.

One tree, a current directory, and a home

The root directory is /. Every path starts there, even when a disk, network share, or removable volume is mounted somewhere below it. Unlike drive-letter systems, Unix presents one namespace. The precise layout is a convention rather than a guarantee: Linux commonly has home directories below /home, while macOS commonly uses /Users; BSD installations may make different administrative choices.

Each shell has a current working directory. A name without a leading slash is interpreted relative to it. Ask where you are with pwd, and move with cd:

$ pwd
/home/lee/projects
$ cd /tmp
$ pwd
/tmp
$ cd                 # with no operand, go to your home directory
$ cd "$HOME"         # the explicit, portable spelling

$HOME is an environment variable containing your login home directory. Many shells also expand ~ at the beginning of an unquoted word, so cd ~ is convenient, but "$HOME" is clearer in scripts. Quoting prevents unwanted splitting when a path contains spaces.

Absolute and relative paths

PathMeaning
/etc/hostsAn absolute path: start at the root.
notes/today.txtA relative path: start at the current directory.
.The current directory.
..The parent directory.
../archiveA sibling-area path: first go up, then into archive.

Slashes separate path components. Multiple slashes are usually treated like one, but write normal paths for readability. cd .. moves up one level; at /, its parent is still /. A leading ./ explicitly says “the item here”, as in ./report.txt. This is especially useful when an item name begins with -.

Listing what is there

ls lists directory entries. Start simply; long listings are useful for inspection but are not a stable machine-readable format.

$ ls
drafts  images  todo.txt
$ ls -l todo.txt       # type, permissions, links, owner, size, time, name
$ ls -a                # include names beginning with .
$ ls -ld "$HOME"       # describe this directory itself, not its contents

Names beginning with . are hidden by convention, not protected. Configuration such as ~/.profile commonly uses this convention. The special entries . and .. appear with ls -a; do not try to remove or rename them.

Options are not perfectly uniform. For example, GNU ls may offer colour-related options that BSD and macOS spell differently. The basic -a, -l, and -d usages above are broadly available.

Creating, copying, renaming, and removing

These commands operate on names, not on an abstract “document”. Confirm pwd and use ls before destructive work.

CommandWhat it does
mkdir reportsCreate one directory.
mkdir -p work/2026/septemberCreate missing parent directories too.
touch empty.txtCreate an empty regular file if absent; otherwise update its timestamp.
cp source.txt copy.txtCopy a file.
cp -R source-dir backup-dirCopy a directory tree. Check local documentation for option details.
mv old-name new-nameRename, or move when the destination is another directory.
rmdir empty-dirRemove an empty directory only.

cp and mv can overwrite a destination without asking. Use distinct destination names, inspect first, or request an interactive confirmation with -i when appropriate. Do not rely on an alias that happens to add safety options: scripts and another system may not have it.

$ cp -- "$HOME/notes/plan.txt" "$HOME/notes/plan.backup.txt"
$ mv -- "draft report.txt" "final report.txt"
$ rm -i -- "unwanted file.txt"

rm removes directory entries; on ordinary local filesystems there is no universal undo or trash. rm -i asks before each removal. rm -r recursively removes a directory and all of its contents, and deserves exceptional care. Never paste a recursive removal command until you have expanded and inspected its target. Avoid rm -rf as a habit; it suppresses useful warnings. The -- above ends option processing, so a filename such as -draft.txt is treated as a filename. Most current GNU, BSD, and macOS versions support it; if it is unavailable, use ./-draft.txt.

Globs: the shell chooses the names

In an unquoted command argument, the shell normally expands wildcard patterns before it starts the program. This is called pathname expansion, or globbing. The program receives a list of resulting names, not the literal asterisk.

$ printf '%s\n' *.txt       # shell expands this to every matching .txt name
$ printf '%s\n' '.*'        # quoted: program receives the two characters .*
$ cp -- *.jpg images/        # copies every matching image, if there are matches

* matches any sequence of characters, ? matches one character, and [0-9] matches one character from a set or range. By default, a pattern such as * does not match leading-dot names. Shell behaviour when nothing matches differs: many POSIX-style shells leave the pattern unchanged, while shells can be configured otherwise. Preview with printf '%s\n' -- pattern before using a destructive command. Quote a path when you mean it literally: rm -- "$file", not rm $file.

What kind of file is it?

A filename extension is a hint, not proof. The file utility examines content and reports a likely type:

$ file -- README
README: UTF-8 Unicode text
$ file -- image.dat
image.dat: PNG image data, 800 x 600, 8-bit/color RGBA, non-interlaced

file is common on Linux, BSD, and macOS, though its recognition database and wording vary. Treat its result as a useful diagnosis, not a security decision. Do not send an unknown binary to a terminal with cat.

Finding names below a directory

find walks a directory tree and tests each entry. Begin with a directory you own, rather than searching all of /, which can be slow and produce permission errors.

$ find "$HOME/Documents" -type f -name '*.txt'
$ find . -type d -name 'draft*'
$ find . -type f -mtime -7

Quote the pattern after -name; otherwise the shell expands it in the current directory before find sees it. -type f means regular files and -type d means directories. Time tests and many advanced predicates have implementation-specific details, so read the local find manual before automating deletion. Prefer reviewing results and acting on an individual path; find ... -exec rm is powerful and easy to misuse.

Symbolic links are references

A symbolic link is a small special file containing another path. Create one with ln -s. It is not a copy, and it can become dangling if its target is moved or removed.

$ ln -s "$HOME/projects/current/README.txt" "$HOME/README-current"
$ ls -l "$HOME/README-current"
lrwxr-xr-x  ... README-current -> /home/lee/projects/current/README.txt
$ cat "$HOME/README-current"

Relative link targets are interpreted from the link’s containing directory, which can make a project tree portable. ls -l shows a link with l as its first mode character and an arrow to its target. Following links during a recursive copy or search has platform- and option-specific consequences; use the local manual and make that choice deliberately.

Safe practice: build a disposable tree

This exercise uses mktemp to create a unique directory rather than reusing a fixed name that might contain old work. It requires no administrator access and does not touch system files. Copy it one command at a time and watch pwd and ls change.

$ (
> practice=$(mktemp -d "${TMPDIR:-/tmp}/unix-navigation.XXXXXX") || exit 1
> [ -d "$practice" ] || exit 1
> printf 'Practice directory: %s\n' "$practice"
> mkdir "$practice/inbox" "$practice/archive" "$practice/notes" || exit 1
> cd "$practice" || exit 1
> printf '%s\n' "Buy tea" "Read manual pages" > notes/todo.txt
> touch inbox/.keep inbox/receipt-01.txt inbox/receipt-02.txt
> pwd
> ls -la inbox
> cp -- notes/todo.txt archive/todo-copy.txt
> mv -- inbox/receipt-01.txt archive/
> find . -type f -name '*.txt'
> ln -s notes/todo.txt todo-current
> file -- todo-current
> cd "$HOME" || exit 1
> rm -ri -- "$practice"
> )

The parentheses run the lab in a subshell, so exit 1 stops only that lab if setup fails rather than closing the interactive terminal or continuing in the wrong directory. mktemp is common on current Linux, BSD, and macOS systems, though it is not specified by POSIX and details vary; consult man mktemp locally. The final command uses the exact unique path and remains intentionally interactive. Read the path it displays before answering. Keeping the directory for later practice is equally valid.

Useful habits

  • Use pwd before modifying or deleting a path.
  • Quote variables and names that may contain spaces: "$HOME", "$file".
  • Use -- (or a ./ prefix) for names that could begin with -.
  • Preview a glob with printf '%s\n' -- *.log before passing it to rm or mv.
  • Use man pwd, man find, and your shell’s help for the version installed on your system.