Resource Cheatsheet
The Linux Command Line 7: Seeing the World as the Shell Sees It
TLCL chapter 7 — pathname, tilde, arithmetic, parameter and command expansion, plus double quotes, single quotes and backslash escaping.
- Updated
- Reading time
- 6 min read
- Detail
- The Linux Command Line
- Detail
- Ch. 7
On this page
In one line: the shell expands your line before the program runs — globs,
~,$var,$(( ))and$( )are all gone by the time the command starts — and quoting is how you decide which of those expansions happen.
Companion to: Shotts, The Linux Command Line (2nd ed.), chapter 7. The full version is the Seeing the World as the Shell Sees It class notes; the section overview is the Linux chapter 7 sheet.
Preview everything
echo rm *.log # read the expanded list, THEN run it for real
ls -l *.log # even better for destructive work: sizes and dates tooThe program never sees * — it receives a finished list of filenames and cannot warn you.
The five expansions
| Expansion | Form | Produces |
|---|---|---|
| Pathname (globbing) | * ? [abc] [!abc] [[:digit:]] |
Names of existing files, sorted as text |
| Tilde | ~ · ~user |
A home directory path |
| Arithmetic | $((2 + 2)) |
An integer result |
| Parameter | $var · ${var} |
A variable’s value (empty if unset) |
| Command | $(cmd) · `cmd` |
The command’s output |
Pathname expansion
| Pattern | Matches (dir: Makefile a.txt b.txt c.log data1 data2 data10 .hidden) |
|---|---|
* |
Makefile a.txt b.txt c.log data1 data10 data2 |
*.txt |
a.txt b.txt |
[ab].txt |
a.txt b.txt |
data? |
data1 data2 — data10 has two chars after data |
- Hidden files are excluded —
.hiddenisn’t in*. Need a pattern starting with a dot. - Sorted as text, not numbers —
data10comes betweendata1anddata2. - No match = pattern passed through literally. That’s how you get a file called
*.xyz. - Brace expansion generates text rather than matching files:
{A,B}-{1,2}→A-1 A-2 B-1 B-2·{01..05}→01 02 03 04 05·mkdir -p proj/{src,docs,tests}.
Tilde, arithmetic, parameter, command
| Written | Result |
|---|---|
~ / ~root |
/root (that user’s home) |
~nosuchuser |
Unchanged literal text — no error |
$((5/2)) $((5%2)) $((2**10)) |
2 1 1024 |
$((7/2)) $((-7/2)) |
3 -3 — truncates toward zero, integers only |
$((1/0)) |
Error, not infinity |
${foo}s with foo=bar |
bars — braces mark where the name ends |
$nosuch |
Empty string, silently |
$(uname -r) |
The command’s output |
Expansions nest: $(( $(echo 3) + 4 )) → 7. For decimals use bc -l, not $(( )).
Quoting
| Form | Globbing | Word splitting | $var $(( )) $( ) |
|---|---|---|---|
| Unquoted | Yes | Yes | Yes |
"double" |
No | No | Yes |
'single' |
No | No | No |
\c |
Next char literal | — | — |
echo this is a test # this is a test (spaces collapse)
echo "this is a test" # this is a test (preserved)
echo "$HOME $((2+2)) $(echo hi)" # /root 4 hi
echo '$HOME $((2+2)) $(echo hi) *' # all literal- Always quote variables. With
f="two words.txt",ls -l $fgives two “No such file” errors;ls -l "$f"works. - Quote command substitution to keep newlines:
echo "$(cat f.txt)"keeps the line break; unquoted collapses it to one line. $still bites inside double quotes:echo "The total is $100.00"printsThe total is 00.00($1is empty).- Single quotes can’t contain a single quote — no escaping inside them. Use
'it'\''s'or double quotes. - Use single quotes for
grep/sed/awkpatterns and anything with$or*meant literally.
Escaping and escape sequences
echo \* # a literal asterisk
echo "Its cost \$100" # Its cost $100
mv two\ words.txt renamed.txt
grep "Failed password" auth.log | \
sort | uniq -c # backslash-newline wraps a long commandInside double quotes, \ escapes only $, `, " and \; elsewhere it stays literal ("hello\\there" → hello\there).
| Sequence | Meaning |
|---|---|
\n \t |
Newline · tab |
\\ |
Literal backslash |
\a \b \r |
Bell · backspace · carriage return |
\0nnn |
Character with octal value nnn |
Plain echo doesn’t interpret these — use printf "a\tb\n" (portable) or echo -e.
🔐 Security notes
- Unquoted variables are shell injection. Values from filenames, logs or forms get word-split and globbed. Always
"$var". $(...)executes. Never build a command string from untrusted input — pass it as a quoted argument.- Unset = empty, silently.
rm -rf $BACKUP_DIR/*with the variable unset becomesrm -rf /*. Useset -euo pipefail. - Filenames are attacker input: a file named
-rf, or with spaces or newlines, changes what a loop does. Use--andfind -print0 | xargs -0. - Globs match what exists now — a cleanup script’s
*picks up whatever was dropped there since. - Secrets on the command line show in
psand history. Pass via a protected file or stdin.
Practice drills
1. Why doesn't `echo *` show hidden files?
Globs don’t match names starting with a dot unless the pattern starts with one.
2. `data1`, `data2`, `data10` — what does `data?` match?
data1 and data2 only. ? matches exactly one character.
3. What does `echo $((7 / 2))` print?
3. Integer arithmetic only — division truncates toward zero.
4. `foo=bar`; why does `echo $foos` print nothing?
The shell looks for a variable named foos, which is unset (and unset means empty, no error). Use ${foo}s.
5. Which expansions survive double quotes?
Parameter ($var), arithmetic ($(( ))) and command substitution ($( )). Globbing and word splitting don’t.
6. `ls -l $file` gives two "No such file" errors but the file exists. Why?
The filename has a space and the variable is unquoted — word splitting made it two arguments. Use "$file".
7. Why does `echo "The total is $100.00"` print `The total is 00.00`?
$1 is a parameter expansion and is empty. Use \$ or single quotes.
8. How do you grep for the literal `$100.00`?
grep -F '$100.00' file — single quotes stop the shell, -F stops grep treating $ and . as regex.
Key takeaways
- The shell expands first; the program never sees
*,~,$varor$(cmd). - Globs match existing files only, skip dotfiles, sort as text, and pass through unchanged on no match.
$(( ))is integers only;$varunset is silently empty;$(cmd)substitutes output.- Double quotes block globbing and splitting but let
$through; single quotes block everything. echo rm *beforerm *, and quote every variable.