Skip to content
daniel@MacBookPro:~
Shell & TerminalDeep Dive August 27, 2026 3 min read

How Shell Expansion and Globbing Actually Work

By the time a command you typed actually runs, the shell has already rewritten it — expanding variables, substituting command output, and turning wildcard patterns into real filenames. Here's the exact order that happens in.

The command you type is rarely the command that actually runs — before execution, the shell performs a specific, ordered sequence of expansions and substitutions, rewriting your input into its final form.

Why the order of expansion actually matters

Bash performs expansions in a defined sequence: brace expansion, tilde expansion, parameter/variable expansion, command substitution, arithmetic expansion, word splitting, and finally filename (glob) expansion — in that order. Getting the mental model of this sequence right explains behavior that otherwise looks inconsistent, like why quoting a variable changes whether word splitting happens to its expanded value.

Brace expansion: pure text generation, no filesystem involved

echo file{1,2,3}.txt
# expands to: file1.txt file2.txt file3.txt

Brace expansion happens purely as text manipulation, before the shell ever checks the filesystem — file{1,2,3}.txt expands to those exact strings whether or not any of those files actually exist.

Parameter expansion: more than just $variable

echo ${name:-default}    # use "default" if $name is unset or empty
echo ${name#prefix}      # remove shortest matching prefix
echo ${#name}            # string length

Beyond simple variable substitution, parameter expansion supports default values, prefix/suffix removal, and string length — a surprisingly capable text-manipulation toolkit that’s easy to overlook if you only ever use $variable in its simplest form.

Command substitution: running a command for its output

echo "Today is $(date +%A)"

$(...) runs the enclosed command and substitutes its standard output in place — the older backtick syntax (`date +%A`) does the same thing but nests and reads far less clearly for anything beyond a trivial case.

Word splitting: the step that quoting protects against

After expansion, the shell splits the resulting text on whitespace (or the characters in $IFS) into separate words — this is exactly the step that turns an unquoted $variable containing spaces into multiple separate arguments, rather than the one argument you likely intended.

Filename expansion (globbing): the final step, and the only one touching the filesystem

ls *.txt
ls file?.txt
ls file[0-9].txt

Globbing happens last, and it’s the only expansion step that actually checks the filesystem — * matches any sequence of characters, ? matches exactly one character, and [...] matches any single character within the specified set, all resolved against files that actually exist in the relevant directory.

Why an unmatched glob pattern doesn’t always error

echo *.xyz
# if no .xyz files exist, this prints the literal string "*.xyz"

By default, an unmatched glob pattern is passed through literally rather than causing an error or expanding to nothing — a frequent source of confusion in scripts that assume a glob will either match real files or disappear entirely, when the actual default behavior is neither.

Why understanding this sequence prevents a whole class of scripting bugs

Bugs involving “why did my variable turn into multiple arguments,” “why didn’t my wildcard match what I expected,” or “why does quoting change this command’s behavior” almost always trace back to a step in this expansion sequence behaving exactly as specified, just not as the script’s author expected — knowing the actual order (and which steps touch the filesystem versus which are pure text manipulation) turns these from mysterious bugs into predictable, fixable behavior.