Home← Linux from ScratchCheat sheet
M8Linux from Scratch

M8 — Bash expansions and globs

Time: about 45–75 minutes
Lab root: /tmp/linux-lab-pushpjeet

Prerequisites#

  • Modules M3–M5 (paths, listing, creating files)
  • A bash shell (echo $SHELL should mention bash)

Learning objectives#

By the end of this module you will be able to:

  1. Generate lists of names with brace expansion
  2. Expand your home directory with tilde expansion
  3. Use parameter expansion for variables ($VAR, ${VAR})
  4. Capture command output with command substitution ($(…))
  5. Match filenames with pathname expansion (globs: * ? [])
  6. Locate a binary with which and nest it inside another command

The shell expands before the command runs#

When you type a line and press Enter, bash does several expansion steps, then runs the resulting words as a command. That is why echo *.txt can print many filenames — echo never saw the star; bash already replaced it.

We will walk the expansions you use every day, with demos under /tmp/linux-lab-pushpjeet.

mkdir -p /tmp/linux-lab-pushpjeet
cd /tmp/linux-lab-pushpjeet

Brace expansion {…}#

Braces generate strings before looking at the disk. They are great for creating several related names at once.

echo {dev,staging,prod}.conf

Real output:

dev.conf staging.conf prod.conf

Ranges work too:

echo {1..5}
echo {a..c}{1..2}
1 2 3 4 5
a1 a2 b1 b2 c1 c2

Create three files in one shot:

mkdir -p /tmp/linux-lab-pushpjeet/brace-demo
cd /tmp/linux-lab-pushpjeet/brace-demo
touch {alpha,beta,gamma}.txt
ls
alpha.txt
beta.txt
gamma.txt

Braces do not need matching files to exist. That is the difference from globs.


Tilde expansion ~#

~ expands to your home directory. ~username expands to that user’s home (when the account exists).

echo ~

Example lab output:

/home/box

On your machine you will see /home/yourname (or similar). Use ~ in paths:

ls ~/Documents   # if that folder exists
echo "Home is $HOME"   # $HOME is the same idea as ~

Parameter expansion $VAR and ${VAR}#

Variables hold text. You assign without a dollar sign and expand with one:

LAB=linux-lab-pushpjeet
echo "Lab name: $LAB"
echo "Length: ${#LAB}"
echo "Unset safe: ${MISSING:-default-value}"

Real output:

Lab name: linux-lab-pushpjeet
Length: 19
Unset safe: default-value

Use ${VAR} when the name sits next to other characters: ${LAB}_backup is clear; $LAB_backup would look for a variable named LAB_backup.

(We go deeper on variables in M10. Here the goal is recognising $… as an expansion.)


Command substitution $(…)#

Run a command and paste its output into the current line:

echo "Today is $(date +%Y-%m-%d)"
echo "Shell binary: $(which bash)"

Real output from the lab:

Today is 2026-09-26
Shell binary: /usr/bin/bash

Nesting is common. List details for the bash binary:

ls -l $(which bash)

Example (fields may vary slightly by distro):

-rwxr-xr-x 1 root root ... /usr/bin/bash

Older scripts use backticks `cmd`. Prefer $(cmd) — it nests cleanly.


Pathname expansion (globs)#

Globs match existing pathnames:

Pattern Matches
* any string (including empty), within one directory level
? exactly one character
[abc] one character from the set
[0-9] one digit in the range
mkdir -p /tmp/linux-lab-pushpjeet/globs
cd /tmp/linux-lab-pushpjeet/globs
touch file1.txt file2.txt notes.md data.csv report2.log

ls *.txt
ls file?.txt
ls *[12]*

Real output:

file1.txt
file2.txt
file1.txt
file2.txt
file1.txt
file2.txt
report2.log

If nothing matches, bash usually leaves the pattern as a literal word (depending on options). That surprises beginners — if ls *.bak says “No such file”, the star did not expand.


which — where is this command?#

which bash
which ls

Useful when several tools share a name or when you want a full path for scripts and nesting: ls -l $(which echo).


Putting expansions together#

A practical one-liner mindset:

# Generate env-specific filenames, then create them
cd /tmp/linux-lab-pushpjeet
touch app-{dev,prod}.env
ls app-*.env

# Show date-stamped message
echo "Lab check at $(date +%H:%M) in $PWD"

Remember the rough order: braces first, then tilde, parameters, commands, and finally globs. You do not need to memorise the full POSIX list on day one — just know that the shell rewrites your line before the program starts.


Common mistakes#

  1. Expecting braces to filter files — {a,b} always generates both strings; use globs to match what exists.
  2. Quoting too early — echo "*.txt" prints the star; quotes suppress globbing (more in M9).
  3. Spaces around = in assignment — LAB = value is wrong; use LAB=value.
  4. Confusing $(…) with (…) — parentheses alone start a subshell; dollar-parentheses capture output.
  5. Globs across directories — * does not cross / unless you use ** with special options (skip that for now).

Hands-on lab#

mkdir -p /tmp/linux-lab-pushpjeet/m8-lab
cd /tmp/linux-lab-pushpjeet/m8-lab
  1. Create host-{web,db,cache}.cfg with one touch using braces. Confirm with ls.
  2. Print your home directory with echo ~ and with echo $HOME.
  3. Set COURSE=linux-from-scratch and echo Course=${COURSE}.
  4. Print Binary: $(which mkdir).
  5. Create a1.txt a2.txt b1.txt and list only a?.txt.

Verify: ls host-*.cfg shows three files; ls a?.txt shows a1.txt and a2.txt.


Practice: check your understanding#

Multiple choice#

1. What does echo {a,b}.txt print (assuming default bash)?

  • A. Only files that exist
  • B. a.txt b.txt
  • C. {a,b}.txt
  • D. An error
Answer

B. Brace expansion generates both names whether or not the files exist.

2. Which form captures the output of date?

  • A. @(date)
  • B. $(date)
  • C. #{date}
  • D. date$
Answer

B. $(date) is command substitution.

3. The pattern file?.log matches which name?

  • A. file.log
  • B. file12.log
  • C. fileA.log
  • D. file/.log
Answer

C. ? is exactly one character, so fileA.log fits. file.log has zero chars in that spot; file12.log has two.

4. Why use ${NAME}_old instead of $NAME_old?

  • A. It is faster
  • B. It clearly ends the variable name before _old
  • C. Braces enable globs
  • D. It disables tilde expansion
Answer

B. ${NAME} delimits the variable name so _old is literal text.

5. What does ~ expand to?

  • A. The current directory
  • B. /tmp
  • C. Your home directory
  • D. The root filesystem
Answer

C. Tilde expands to the current user’s home directory.

Match the columns#

Syntax Role
1 {dev,prod} A Match existing filenames
2 ~ B Generate strings
3 $VAR C Home directory
4 $(cmd) D Variable value
5 *.txt E Insert command output
6 which bash F Print path of executable
Answer key

1→B, 2→C, 3→D, 4→E, 5→A, 6→F

Fill in the blank#

  1. Create three files in one command: touch report_{2024,2025,2026}.csv
  2. Show today’s date with substitution: echo $(date +%F)
  3. List one-character suffix logs: ls log?.txt
  4. Safe default for an unset variable: echo ${CITY:-Unknown}
Answer key
  1. touch report_{2024,2025,2026}.csv (braces as shown)
  2. $(date +%F) or similar date format inside $()
  3. log?.txt (question mark)
  4. ${CITY:-Unknown} (parameter default)

Mini terminal challenge#

Goal: Generate a small project tree with expansions only.

Setup

rm -rf /tmp/linux-lab-pushpjeet/m8-challenge
mkdir -p /tmp/linux-lab-pushpjeet/m8-challenge
cd /tmp/linux-lab-pushpjeet/m8-challenge

Tasks

  1. mkdir -p {src,tests,docs}
  2. touch src/main-{dev,prod}.sh tests/test-{1..3}.sh
  3. echo "Built on $(date +%Y-%m-%d) by $USER" > docs/BUILD.txt
  4. ls -R and confirm the names.

Verify: You see six shell files under src/tests and a BUILD.txt containing today’s date.

Stretch: ls $(which touch) and explain what each expansion did.


Next: M9 — Redirection, pipes, and quoting — send output places, chain tools, and quote safely.

Continue

← Linux from Scratch hub · Cheat sheet · All tutorials

Newsletter

Liked this? Get the next tutorial by email

New tutorials, labs and cheat sheets, plus AWS batch dates and Oracle tips if you want them. No spam, unsubscribe anytime.

I’m interested in

Double opt-in: you’ll get a confirmation email first. Privacy policy

WhatsApp