PLAINFINCH learn by building

Wildcards

You will use patterns like `*.jpg` to act on many files at once, and preview the matches before anything changes.

What is a wildcard?

A wildcard is a character that stands for other characters in a file name. Instead of typing four photo names, you type *.jpg and the shell fills in every name that fits. The proper name for this is globbing, and the patterns are called globs, though most people say wildcards.

Bash knows three wildcards. * matches any run of characters, including none at all. ? matches exactly one character. [ ] matches one character from the set inside the brackets, such as [123] or a range such as [a-f]. Everything else in the pattern has to match letter for letter, and capital letters count.

In the bakery folder, *.jpg matches all four photos, specials.* matches specials.txt and specials.txt.bak, and in the Break It folder order-?.txt matches order-1.txt but never order-12.txt.

ls *.jpg
ls specials.*
ls order-?.txt
ls order-[12].txt

Who expands the wildcard, the shell or the command?

The shell does, before the command starts. When you type rm *.bak, bash looks at the folder, finds the names that end in .bak, and replaces the pattern with them. rm never sees the star. It receives rm menu.csv.bak orders-old.bak specials.txt.bak, exactly as if you had typed those three names yourself.

This one fact explains most wildcard behaviour. It means every command gets wildcards for free, from ls and mv to programs written last week. It also means a command can’t warn you that a pattern matched too much, because by the time it runs the pattern is gone and only a list of names is left.

The Explain view in this practice terminal shows the expansion: type a command with a wildcard and look at the list of matched names. On a real machine echo does the same job, which is why it’s the standard way to preview.

FigureYou type rm *.bak. Bash reads the bakery folder, picks the three names ending in .bak and leaves menu.csv and the rest. rm then receives the three names and never sees the star.You type rm *.bak. Bash reads the bakery folder, picks the three names ending in .bak and leaves menu.csv and the rest. rm then receives the three names and never sees the star.
The shell swaps *.bak for the matching names before rm starts. That is why echo *.bak is a true preview: it gets exactly the list rm would get.

How do you preview a pattern before it does anything?

Put echo or ls in front of the same pattern. echo *.bak prints the names the shell would hand to any command, on one line, and changes nothing. ls *.bak prints the same names in a list and says so clearly when there are none.

Make it a fixed habit for rm and mv: type the pattern with echo first, read the result, then press the Up arrow, replace echo with rm, and press Enter. Because you only change the first word, the pattern you run is the one you checked. In this folder the preview prints:

Three names, all backups, and menu.csv is not among them. That is the moment to delete. If the preview had shown menu.csv you would know the pattern was too loose, and you would have lost nothing by looking.

echo *.bak
menu.csv.bak orders-old.bak specials.txt.bak

rm *.bak

What happens when nothing matches?

Bash has an odd rule here. When a pattern matches no file at all, the shell leaves it exactly as you typed it, star included, and passes it on. The command then looks for a file literally called *.png and fails. In this terminal rm *.png prints rm: cannot remove '*.png': No such file or directory.

When you see a star or question mark inside quotes in an error, read it as “this pattern matched nothing here”. Usually you are in the wrong folder, the files end differently from what you expected, or the case is wrong, such as .JPG against *.jpg. Check with pwd and ls.

On a Mac, zsh is stricter. It stops with zsh: no matches found: *.png and doesn’t run the command at all. The cause is the same, and the fix is the same: look at the real names.

Why do quotes stop a wildcard?

Single or double quotes tell the shell to leave the characters inside alone. echo "*.bak" prints *.bak with no expansion, because the star is protected. A backslash does the same for one character: echo \*.bak also prints the star.

You will need this when a command wants the pattern itself. The find command in the next lesson takes a pattern and does its own matching, deep inside folders. If you leave the pattern unquoted, bash expands it first in the current folder and find receives the wrong thing. Writing find . -name "*.bak" keeps the pattern intact for find.

Quotes also keep names with spaces in one piece. ls shop front.jpg looks for two files, shop and front.jpg. ls "shop front.jpg" looks for one. Wildcard matches are already safe on this point: if *.jpg matches shop front.jpg, the shell passes it as a single name.

echo "*.bak"
*.bak

Do wildcards match hidden files?

No. A plain * skips names that start with a dot, such as .bashrc or the .DS_Store files a Mac leaves in every folder. This protects you: rm * in your home folder won’t touch your settings files. It also surprises people who expect mv * ../new/ to move everything and find the hidden files left behind.

To match hidden files you write the dot yourself, as in ls .D*. Avoid the pattern .* with rm. In older shells it also matched .., the folder above, and even where bash now protects you, it is an easy way to delete more than you meant. Name the hidden files you want instead.

How do ? and [ ] help with numbered files?

Numbered files are where * gets clumsy. The bakery keeps orders as order-1.txt, order-2.txt and so on up to order-12.txt. The pattern order-*.txt matches all of them. order-?.txt matches only the single-digit ones, because ? is one character, and order-??.txt matches only the two-digit ones.

Brackets pick from a set. order-[123].txt matches orders 1, 2 and 3. A range such as IMG_204[0-5].JPG matches up to six camera files, IMG_2040.JPG to IMG_2045.JPG. Put ! or ^ first to mean “not these”: order-[!1].txt matches every single-digit order except order 1.

Wildcards sort their matches alphabetically, which puts loaf-10.jpg before loaf-2.jpg. This is why people name files loaf-01.jpg, loaf-02.jpg: with leading zeros, alphabetical order and number order agree.

echo order-?.txt
order-1.txt order-2.txt order-3.txt

What do AI tools get wrong with wildcards?

AI assistants often suggest a cleanup such as rm -rf *.log *.tmp ./* or rm -f backup* without knowing what is in your folder. The command is valid, and it may match far more than the reply intends. Before running any suggested command with * in it, run the same pattern with echo and read every name.

They also mix wildcards with regular expressions, the pattern language used by grep and sed. In a regular expression .* means any text, but as a shell wildcard .* means hidden files. A suggested ls .*\.jpg will not do what it says. If a pattern contains \., .* or +, ask which language it is written in.

Finally, watch the case. Replies tend to assume *.jpg when phones and cameras often save .JPG or .jpeg. A quick ls shows the real endings, and you can match both with *.[jJ][pP][gG].

Field notes

  • * matches any run of characters, so *.jpg means every name ending in .jpg.
  • The shell swaps the pattern for matching names before the command runs.
  • Put echo or ls in front of a pattern to see what it matches first.
  • ? matches exactly one character, which helps with numbered files.

Easy to get wrong

menu* matches menu.csv as well as menu.csv.bak. Make the pattern as specific as the files you mean, and preview it with echo before deleting.

Wildcards are case-sensitive on Linux. Photos saved with .JPG are skipped. If one pattern then matches nothing, mv reports it but still moves the rest.

Without quotes the shell expands *.bak in the current folder first, so find gets file names instead of a pattern. Quotes pass the pattern through untouched.

* matches any number of characters, so it also catches order-12.txt. ? matches exactly one character.

Questions people ask

Is a wildcard the same as a regular expression?

No. Wildcards are a small pattern language for file names, expanded by the shell. Regular expressions are a larger language for text, used by grep and sed. Both use *, with different meanings, which is a common source of confusion.

Can I undo `rm *.bak`?

Not from the terminal. rm removes files without a bin. That is why the preview with echo comes first, and why the Deleting safely lesson suggests rm -i when you are unsure.

Does `*` look inside subfolders?

No. *.jpg only matches names in the current folder. To match deeper, give a path such as */*.jpg, or use find, which searches every folder below. In bash, shopt -s globstar turns on ** for any depth; zsh has it built in.

Why did `echo *.png` print `*.png`?

Nothing in the folder ends in .png, so bash left the pattern as you typed it. That output is a signal that the pattern matched nothing.

Do wildcards work the same on a Mac?

Mostly. zsh expands *, ? and [ ] the same way, but stops with “no matches found” instead of passing an unmatched pattern through. Mac disks usually ignore case when you open a file by name, but zsh wildcards still match case by default, so *.jpg skips .JPG files there too.

Keep going