Globbing and Wildcards: Pattern Matching Files in Linux

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

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. grep uses 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.

Lina: Senpai, when I type ls *.txt, only the .txt files show up. What does that * mean?
Linny-senpai: Good question. * is the symbol for "any character, any number of times".
Linny-senpai: And here is the key point: it is the shell, not ls, that processes the *.
Lina: Wait, not ls?
Linny-senpai: Right. The moment you press Enter, the shell looks at *.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
Lina: So *.txt means "something + .txt".
Linny-senpai: Exactly. And remember, * matches zero characters too. So * on its own means "everything".
Lina: Then what about re*?
Linny-senpai: That means "starts with re", so 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
Lina: So ? is just one character. Why did log10.txt drop out?
Linny-senpai: Because log?.txt means "log, then exactly one character, then .txt".
Linny-senpai: log10.txt has two characters there, 1 and 0. So it does not match.
Lina: And if I want two characters?
Linny-senpai: Write ??. 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
Lina: So [0-9] means "0 through 9". Can I do letters too?
Linny-senpai: You can. [a-z] for lowercase and [A-Z] for uppercase. You can also combine them, like [a-zA-Z0-9].
Linny-senpai: Just remember that it always matches exactly one character.

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}.txt
file1.txt file2.txt file3.txt
Lina: Hold on, is this not the same as globbing?
Linny-senpai: It looks similar but it is completely different. {} only builds strings, and it does not check whether the files exist.
Linny-senpai: So it produces names even for files that are not there.
Lina: While globbing only expands to files that really exist.
Linny-senpai: Right. Think of {} 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
Lina: I get the basic symbols. Can I write something like "only .jpg and .png"?
Linny-senpai: With standard globbing that is awkward. Enable extglob (extended globbing) and it gets much easier.
Linny-senpai: Use @(...) 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
Lina: The error message shows *.xml unchanged. Did the * stop working?
Linny-senpai: It did not stop working. It chose not to expand.
Lina: Not expanding is an option?
Linny-senpai: It is. By default, when nothing matches, bash passes the pattern along as plain text. So ls answered that no file by that name exists.
Lina: That changes how I read the error. It is a signal that there were zero matches. Got it.
Linny-senpai: Exactly. And 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.

Lina: I have the theory. I want to try it myself.
Linny-senpai: Good, I prepared three exercises. Create the practice files first, then give them a go.

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

Lina: Let me sum up. It is the shell, not the command, that expands a glob.
Linny-senpai: Right. The command only receives the filenames after expansion.
Lina: * is any number of characters, ? is exactly one, and [] picks one from a set. I have that.
Linny-senpai: That's it. And before you pair one with rm, check the same pattern with ls. Never skip that.

Today's 3-Line Summary

  1. The shell expands a glob, not the command. The command receives the names after expansion
  2. * matches zero or more characters, ? exactly one, and [] one character from a set
  3. Before pairing a pattern with rm, check it with ls first

Next Reading

Share this article

Next steps