Globbing and Wildcards: Pattern Matching Files in Linux
What you'll be able to do
- Use the three basic symbols `*`, `?`, and `[]` for the right job
- Explain that the shell, not the command, expands a glob
- Avoid the traps around no-match behavior, hidden files, and quoting
Prerequisites (read these first)
What You'll Learn
- That globbing (wildcards) is a job done by the shellAn interactive program that reads the commands you type and runs them., not the command
- How to use the three basic symbols
*,?, and[] - How brace expansion
{}is different - How to avoid traps with
extglob, hidden files, and no-match behavior
Words used here (sorted out first)
- Glob: a way to name many files at once. Wildcard and pattern mean roughly the same thing. This article says "glob" throughout.
- Regular expression: a way to search inside text.
grepuses them. It is a different mechanism from a glob. The symbols look alike, which is exactly why people mix them up. - Expansion: the shell replacing the symbols you wrote with real filenames.
- Hidden file: a file whose name starts with
.. The classic example is.bashrc.
In a glob, * means "any number of characters". In a regular expression, it means "repeat the character before it". Same symbol, different meaning.
Quick Summary
- Any characters, any count →
*(e.g.*.txt) - Exactly one character →
?(e.g.file?.log) - One char from a set →
[](e.g.img[0-9].png) - The shell expands the pattern, not the command
1. What Is Globbing?
Conclusion: The shell expands wildcards into filenames before the command runs.
ls *.txt, only the .txt files show up. What does that * mean?* is the symbol for "any character, any number of times".ls, that processes the *.ls?*.txt. It rewrites it into the filenames that actually exist, and hands those to ls. We call that glob expansion.How expansion works
If the current directoryA container that organizes files. Same idea as a "folder" on Windows or macOS. has a.txt, b.txt, and memo.md:
You type: ls *.txt Shell expands: ls a.txt b.txt <- memo.md is excluded ls receives: a.txt b.txt
ls knows nothing about *. It just receives the expanded filenames.
Globbing matches filenames. Regular expressions search inside text, and .* in grep is one. They are different mechanisms. Do not mix them up.
First, about avoiding accidents
A glob is powerful together with rm. It is also dangerous. A file removed with rm does not go to a trash can the way it does in a GUI. It is gone at once.
So make a habit of checking the same pattern with ls before you run rm.
$ ls *.tmp # see with your own eyes what the pattern covers $ rm *.tmp # remove only once you are satisfied
The virtual terminal on this site is for practice. Nothing you type there can delete a file on your own computer, so try things freely.
2. The * Asterisk: Any String
Conclusion: The
*matches zero or more characters, but never a leading dot.
$ ls
a.txt b.txt data.csv report.txt notes.md
# Only files ending in .txt $ ls *.txt
a.txt b.txt report.txt
*.txt means "something + .txt".* matches zero characters too. So * on its own means "everything".re*?report.txt matches. You can put * at the start, the end, or the middle.# Starts with re $ ls re* report.txt # Contains data (anything before or after is fine) $ ls *data*
data.csv
* does not match a leading dot, which means hidden files are left out.
Running ls * will not show a file like .bashrc. That rule exists to stop rm * from sweeping up your config files by accident.
3. The ? Question Mark: Any Single Character
Conclusion: The
?matches exactly one character, for fixed-length names.
$ ls
log1.txt log10.txt log2.txt log3.txt
# log + one char + .txt (log10.txt is excluded: two chars) $ ls log?.txt
log1.txt log2.txt log3.txt
? is just one character. Why did log10.txt drop out?log?.txt means "log, then exactly one character, then .txt".log10.txt has two characters there, 1 and 0. So it does not match.??. Then log??.txt matches log10.txt.# Two characters $ ls log??.txt
log10.txt
4. The [] Brackets: One Char From a Set
Conclusion: The
[]matches one character from the set, with ranges and negation.
$ ls
img0.png img1.png img2.png img9.png imgA.png
4-1. List the candidates
# 0, 1, or 2 $ ls img[012].png
img0.png img1.png img2.png
4-2. Use a range
# One digit from 0-9 $ ls img[0-9].png
img0.png img1.png img2.png img9.png
[0-9] means "0 through 9". Can I do letters too?[a-z] for lowercase and [A-Z] for uppercase. You can also combine them, like [a-zA-Z0-9].4-3. Negate it (! or ^)
# One character that is NOT a digit $ ls img[!0-9].png
imgA.png
[] syntax reference
| Syntax | Meaning |
|---|---|
[abc] |
One of a / b / c |
[a-z] |
One char a-z (range) |
[0-9] |
One char 0-9 (range) |
[!0-9] |
One non-digit char (negation) |
[a-zA-Z] |
One letter (combined ranges) |
5. Brace Expansion {}: A Separate Mechanism
Conclusion: Brace expansion builds strings whether or not the files exist.
# Brace expansion: generates three strings
$ echo file{1,2,3}.txtfile1.txt file2.txt file3.txt
{} only builds strings, and it does not check whether the files exist.{} as "create these in one go", and * or [] as "pick from what already exists".# Create numbered directories at once (they don't need to exist)
$ mkdir log{2024,2025,2026}
# A numeric range works too
$ echo {1..5}1 2 3 4 5
When * or [] match no files at all, the pattern is left as plain text. That is bash's default behavior.
Brace expansion {} is always expanded. The two behave differently, so take care.
6. extglob: Extended Globbing for More Power
Conclusion: extglob enables advanced patterns; it is off by default.
# Enable extended globbing (bash) $ shopt -s extglob
@(...) for "any of" and !(...) for "except".extglob syntax
| Syntax | Meaning |
|---|---|
?(pattern) |
Zero or one occurrence |
*(pattern) |
Zero or more occurrences |
+(pattern) |
One or more occurrences |
@(pattern) |
Exactly one (any of) |
!(pattern) |
Anything except pattern |
Inside pattern, separate the alternatives with |.
# Match .jpg or .png $ ls *.@(jpg|png) # Everything except .txt $ ls !(*.txt)
shopt -s extglob applies only to that shell. Close the terminal and it is gone.
To turn it off right now, run shopt -u extglob.
To have it every time, add the line to your ~/.bashrc. That touches a settings file, so copy it first.
$ cp ~/.bashrc ~/.bashrc.bak $ echo 'shopt -s extglob' >> ~/.bashrc $ source ~/.bashrc
If something goes wrong, cp ~/.bashrc.bak ~/.bashrc puts it back. >> appends to the end of a file; a single > would erase it, so it is not used here.
7. Common Beginner Traps
Conclusion: No-match behavior, hidden files, and quoting are the classic traps.
7-1. Lina gets stuck: no match leaves the pattern as-is
# When there are no .xml files $ ls *.xml
ls: cannot access '*.xml': No such file or directory
*.xml unchanged. Did the * stop working?ls answered that no file by that name exists.shopt -s nullglob switches it to pass nothing at all when there are zero matches.7-2. * does not catch hidden files
# To include hidden files $ shopt -s dotglob $ ls *
With dotglob enabled, * also matches hidden files. Keeping it off is safer for everyday use.
7-3. Quoting disables globbing
# Quoting prevents * from expanding $ ls "*.txt"
ls: cannot access '*.txt': No such file or directory
Wrapping a pattern in quotes, like "*.txt" or '*.txt', stops glob expansion.
That is useful when you want * as a plain character. But if you meant it to expand, the quotes turn it off without warning.
8. Mini Exercises: Try It Yourself
Conclusion: Three tasks on extensions, length, and ranges to practice globbing.
Work inside a fresh empty directory. That way none of your existing files are touched.
# Prepare a practice directory and files $ mkdir -p ~/glob-practice && cd ~/glob-practice $ touch a.txt b.txt c.log data1.csv data2.csv data10.csv
Exercise 1: List only the files ending in .csv.
Show Hint 1 (Direction)
Say "the start of the name can be anything, but the end must be .csv". One symbol covers that start.
Show Hint 2 (Command name)
List with ls. The symbol for "any number of characters" is *.
Show Answer
$ ls *.csv
data1.csv data10.csv data2.csv
The three names ending in .csv appear. a.txt and c.log are left out.
Note that ls orders names as text. That is why data10.csv comes before data2.csv.
Exercise 2: List data + one character + .csv only. Leave out data10.csv.
Show Hint 1 (Direction)
This time the length is fixed. Use the symbol for "exactly one character" instead of "any number".
Show Hint 2 (Command name)
List with ls. The symbol for exactly one character is ?.
Show Answer
$ ls data?.csv
data1.csv data2.csv
data10.csv has two characters there, 1 and 0, so it drops out.
Exercise 3: Show only data1.csv and data2.csv, using brackets.
Show Hint 1 (Direction)
List the characters you want yourself. Use the form that means "one character out of this set".
Show Hint 2 (Command name)
List with ls. The set goes inside []. Write 1 and 2 in it.
Show Answer
$ ls data[12].csv
data1.csv data2.csv
[12] means "one character, either 1 or 2". data10.csv is left out.
When you are done, you can remove the whole practice directory.
-r is the option that means "remove the directory and everything inside it". Add -i and it asks about each item one at a time. Look at the contents with ls first.
$ cd ~ $ ls ~/glob-practice $ rm -ri ~/glob-practice
9. Review
* is any number of characters, ? is exactly one, and [] picks one from a set. I have that.rm, check the same pattern with ls. Never skip that.Today's 3-Line Summary
- The shell expands a glob, not the command. The command receives the names after expansion
*matches zero or more characters,?exactly one, and[]one character from a set- Before pairing a pattern with
rm, check it withlsfirst