Chapter 09Lesson 01~85 minutes

Safe Path Handling and Filename Edge Cases

Filesystem automation is where quoting becomes a safety property. Reliable scripts treat pathnames as opaque data and preserve exact boundaries from discovery through command execution.

BeginnerFilesystemHands-on lab

Learning objectives

By the end of this lesson

  • Handle paths without word splitting or glob expansion.
  • Protect commands from leading-dash filenames.
  • Use NUL-delimited pathname streams.
  • Distinguish symlink objects from targets.
  • Store multiple paths safely in arrays.

1. Treat pathnames as opaque data

A pathname is one shell argument, not a sentence. Quoting prevents word splitting and wildcard expansion from changing it.

file="release candidate.tar.gz"
printf 'path=<%s>\n' "$file"
cp -- "$file" backup/
Default path rule

Quote every pathname expansion such as "$path" and "${files[@]}".

2. Spaces and tabs are legal filename characters

mkdir -p demo
printf 'hello\n' > "demo/api build.log"
printf 'world\n' > $'demo/tab\tname.log'

for file in demo/*.log; do
  printf 'file=%q\n' "$file"
done

A glob can safely produce complete path words. Problems occur when the resulting variable is later expanded without quotes.

3. Filenames can begin with a dash

printf 'payload\n' > ./-dangerous
cat -- ./-dangerous
rm -- ./-dangerous

Many utilities recognize -- as the end of options. Prefixing a relative path with ./ also prevents it from beginning with a dash.

Command-specific behavior

Check whether a utility supports -- when portability matters.

4. Newline is not a safe universal filename delimiter

Unix filenames may contain newline characters, so line-based filename lists are not fully general.

printf 'x\n' > $'demo/line\nbreak.txt'

find demo -type f -print0 |
while IFS= read -r -d '' file; do
  printf 'file=%q\n' "$file"
done
Why NUL works

A Unix pathname cannot contain the NUL byte, so NUL-delimited records are unambiguous.

5. Wildcards can be literal filename characters

printf 'data\n' > 'demo/[prod]*.conf'
literal='demo/[prod]*.conf'
cat -- "$literal"

Quoted expansion keeps [, *, and ? as literal data instead of shell patterns.

6. Relative paths depend on the current directory

script_dir=$(
  cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 &&
  pwd -P
) || exit 1

config="$script_dir/../config/app.conf"

Script-owned assets should usually be resolved relative to the script, not to whichever directory the caller happened to use.

8. Path text is not filesystem identity

Two different strings may resolve to the same file through ., .., hard links, or symlinks. Conversely, naive string normalization can change meaning when symlinks are involved.

Canonicalize only when needed

Use filesystem-aware resolution only when identity matters, and understand the target platform's realpath/readlink behavior.

9. Store multiple paths in arrays

files=(
  "artifacts/api build.tar"
  "artifacts/worker.tar"
  "artifacts/cache*.tar"
)

for file in "${files[@]}"; do
  printf 'path=<%s>\n' "$file"
done

A space-delimited scalar is not a reliable pathname collection.

10. Hands-on lab: hostile pathname inventory

mkdir -p "$HOME/devops-academy/bash/chapter09/lesson01/files"
cd "$HOME/devops-academy/bash/chapter09/lesson01"

printf 'a\n' > "files/space name.txt"
printf 'b\n' > "files/-leading-dash.txt"
printf 'c\n' > 'files/[brackets]*.txt'
printf 'd\n' > $'files/newline\nname.txt'

count=0
while IFS= read -r -d '' file; do
  ((count += 1))
  printf 'path=%q bytes=%s\n' "$file" "$(wc -c < "$file")"
done < <(find files -type f -print0)

printf 'count=%d\n' "$count"

Verification checklist

11. Knowledge check

Question 1. Why quote pathname variables?

Question 2. Why use -- before path operands?

Question 3. What delimiter safely represents arbitrary Unix filenames?

Question 4. Does -e necessarily succeed for a dangling symlink?

12. Summary

Safe filesystem scripting treats pathnames as opaque data. Quote expansions, use option terminators where supported, preserve filename boundaries with NUL when required, and distinguish path text from the filesystem object it resolves to.

13. Further reading

  • GNU Bash Reference Manual — Quoting and Filename Expansion.
  • GNU Coreutils manuals — cp, rm, basename, dirname.
  • GNU findutils manual — -print0.
  • POSIX pathname and utility syntax conventions.
Next lesson

Creating, Copying, Moving, and Removing Content

Continue Chapter 9 by building safer, more predictable filesystem automation.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.