Absolute Paths, Relative Paths, and Path Expansion
A path written in a command is not always the path a program receives. The shell can expand variables, tildes, command substitutions, and wildcard patterns before the kernel resolves the resulting pathname.
Learning objectives
By the end of this lesson
- Distinguish absolute paths from paths resolved relative to a process working directory.
- Explain dot, dot-dot, home, and symbolic-link behavior without relying on visual intuition.
- Identify major Bash expansion stages that can transform command arguments.
- Use quoting to control parameter expansion, word splitting, and pathname expansion.
- Canonicalize and validate paths before performing consequential operations.
1. Pathnames identify objects through a directory hierarchy
A pathname is a sequence of components separated by /.
The kernel resolves components through directories until it reaches
the target object or an error. The object may be a regular file,
directory, symbolic link, socket, device, or another filesystem
type.
flowchart TD
X["Typed path expression"] --> E["Shell expansions"]
E --> A["Argument passed to command"]
A --> B{"Begins with slash?"}
B -->|"yes"| R["Start at root directory"]
B -->|"no"| C["Start at process working directory"]
R --> K["Kernel resolves components and links"]
C --> K
K --> O["Filesystem object or error"]
2. Absolute and relative paths
//etc/ssh/sshd_config names the same pathname from
any working directory
config/app.env depends on where the process is
running
~/logs normally becomes a path under the current
user’s home
Absolute paths improve clarity but can reduce portability when a workspace moves. Relative paths improve portability inside a known project root but become dangerous when that root is not established. Robust automation combines a verified root with derived, quoted paths.
3. Special components: dot, dot-dot, and repeated slashes
.
Current directory component
./script.sh explicitly names a file in the current
directory and bypasses ordinary PATH search.
..
Parent directory component
Resolution moves to the parent of the current directory component, subject to filesystem and symbolic-link semantics.
/
Root and separator
A leading slash selects the root. Repeated slashes are generally treated as one, although portable code should avoid depending on special implementation cases.
Directory expectation
A trailing slash can require the target to resolve as a directory and can change command behavior.
Removing .. components as strings can be wrong when
symbolic links are involved. Use filesystem-aware tools such as
realpath when canonical physical resolution is
required.
4. The shell can transform a path before execution
Bash performs several expansion stages. The exact rules are detailed, but the operational model is essential:
logs/{app,worker} can generate multiple words
before variable expansion.
An unquoted leading ~ can become a home-directory
path.
$HOME, $(pwd), and
$((n+1)) produce text.
Unquoted expansion results may split into multiple arguments
based on IFS.
Unquoted wildcard patterns can become matching filenames.
Syntactic quotes are removed before the external command receives its argument vector.
workspace="$HOME/devops academy"
# One argument because the expansion is quoted.
printf '<%s>\n' "$workspace"
# Potentially multiple arguments because the expansion is unquoted.
# Do not use this form for path variables.
printf '<%s>\n' $workspace
# Tilde expansion occurs here:
printf 'home expression: %s\n' ~
# Quoted tilde is literal text, not a home expansion:
printf 'literal: %s\n' "~"
5. Quoting controls shell interpretation
"$root/config file"
'*.log' when the asterisk
must remain literal
The safest default is simple: quote every parameter expansion that should remain one argument. Exceptions such as intentional arrays, pattern expansion, and arithmetic should be explicit and reviewed.
6. Pathname expansion and wildcard patterns
lab="$HOME/devops-academy/linux/chapter03/lesson03/globs"
mkdir -p -- "$lab"
printf '%s\n' alpha beta > "$lab/app.log"
printf '%s\n' gamma > "$lab/worker.log"
printf '%s\n' hidden > "$lab/.audit.log"
printf '%s\n' text > "$lab/readme.txt"
cd -- "$lab" || exit 1
printf 'Unquoted pattern expands to matching non-hidden names:\n'
printf ' %s\n' *.log
printf 'Quoted pattern remains literal:\n'
printf ' %s\n' '*.log'
printf 'Explicit hidden-name pattern:\n'
printf ' %s\n' .*.log
By default, a leading dot must be matched explicitly. A pattern with
no match may remain literal unless shell options such as
nullglob or failglob change behavior.
Scripts must define and verify the intended rule.
7. Canonicalization and target validation
realpath resolves components and symbolic links
according to its options. readlink -f offers similar
behavior on many GNU/Linux systems, but portability and
missing-target behavior differ.
course_root="$HOME/devops-academy/linux"
requested="$course_root/chapter03/lesson03"
root_real=$(realpath -e -- "$course_root") || exit 1
target_real=$(realpath -m -- "$requested") || exit 1
printf 'root=%s\n' "$root_real"
printf 'target=%s\n' "$target_real"
case "$target_real" in
"$root_real"|"$root_real"/*)
printf 'Target is inside the approved course root.\n'
;;
*)
printf 'Refusing target outside course root: %s\n' "$target_real" >&2
exit 1
;;
esac
A simple prefix test can confuse /srv/app with
/srv/application. Match either the root itself or the
root followed by /.
8. Hands-on lab: observe expansion and resolution
lab="$HOME/devops-academy/linux/chapter03/lesson03"
root="$lab/path lab"
mkdir -p -- "$root/real/releases/v1" "$root/reports"
ln -sfn -- "real/releases/v1" "$root/current"
printf 'version=1\n' > "$root/real/releases/v1/app.env"
cd -- "$root" || exit 1
{
printf '=== working directory ===\n'
printf 'logical=%s\n' "$(pwd -L)"
printf 'physical=%s\n' "$(pwd -P)"
printf '\n=== path forms ===\n'
printf 'absolute=%s\n' "$root/current/app.env"
printf 'relative=%s\n' "current/app.env"
printf 'canonical=%s\n' "$(realpath -e -- current/app.env)"
printf '\n=== argument boundaries ===\n'
printf 'quoted argument: <%s>\n' "$root"
printf 'literal glob: <%s>\n' '*.env'
printf '\n=== matches under current release ===\n'
for file in current/*.env; do
[ -e "$file" ] || continue
printf '%s -> %s\n' "$file" "$(realpath -e -- "$file")"
done
} > "$lab/path-report.txt"
less "$lab/path-report.txt"
Verification checklist
9. Common path-handling mistakes
Using unquoted path variables
Whitespace and wildcard characters can create multiple arguments or unexpected matches. Quote expansions intended as one path.
Trusting a visual path without resolving links
A logical path can traverse symbolic links to another location. Use physical resolution when containment or security depends on the true target.
Assuming a wildcard always matches
Default unmatched-pattern behavior may pass the literal pattern to a command. Guard matches or select explicit shell options.
Building commands with eval
Re-parsing constructed text introduces injection and quoting hazards. Use arrays and direct argument passing instead.
10. Knowledge check
Question 1. At what point is a relative path anchored?
Question 2. Why does "$path" usually
behave more safely than $path?
Question 3. Why is removing
.. components as plain text not a reliable
canonicalization method?
11. Summary
Absolute paths begin at root; relative paths begin at a process’s current directory. Before the kernel resolves a pathname, Bash may expand tildes, variables, substitutions, and wildcard patterns. Quote path variables by default, canonicalize when physical location matters, and validate that resolved targets remain inside an approved root.
12. Further reading
- GNU Bash Reference Manual — shell expansions, quoting, filename expansion, and shell options.
- Linux manual pages for path_resolution, realpath, readlink, glob, and symlink.
- POSIX pathname resolution and Shell Command Language specifications.
- GNU Coreutils manuals for realpath, readlink, dirname, basename, and stat.
- Filesystem Hierarchy Standard — conventional absolute system paths.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.