22  22. Wildcards and Pipes

This page covers two things that make the command line powerful: wildcards (work with many files at once) and pipes (connect commands to each other). The examples use the practice files from Files, Folders, and Paths.

22.1 Wildcards: matching many file names

A wildcard is a special character that stands for other characters in a file name. A name pattern that contains wildcards is often called a glob.

Wildcard Matches Example Matches in the practice files
* any number of characters (even none) music/*.mp3 song1.mp3, song2.mp3
? exactly one character music/song?.mp3 song1.mp3, song2.mp3
[abc] exactly one of the listed characters pictures/[fg]* family.jpg (names starting with f or g)

echo shows what a wildcard matches:

cd ~/Documents/bash-tutorial-files
echo music/*.mp3
echo music/*1*
echo documents/reports/*.txt

Output:

music/song1.mp3 music/song2.mp3
music/song1.mp3
documents/reports/annual-summary.txt documents/reports/quarterly-report.txt

Wildcards work with every command. Remember the example from What Is the Command Line?: moving every file with 2025 in its name takes one command. Try it with two new folders:

mkdir project1 project2
touch project1/budget-2025.xlsx project1/budget-2026.xlsx project1/sales-2025.docx
mv project1/*2025* project2
ls project2

Output:

budget-2025.xlsx  sales-2025.docx

22.1.1 The shell expands wildcards, not the command

The shell replaces a wildcard pattern with the list of matching names before it runs the command. When you type ls *.txt, ls never sees the *; it receives the file names. That’s why echo is a safe way to preview what a pattern will match.

When nothing matches, for example ls *.pdf in a folder without PDF files, the Mac and Git Bash behave differently:

zsh stops with an error and doesn’t run the command at all:

zsh: no matches found: *.pdf

(This also happens if you paste a web address containing ? without quotes. Put quotes around addresses.)

Bash passes the pattern unchanged, so ls looks for a file literally named *.pdf:

ls: cannot access '*.pdf': No such file or directory

Two more details:

  • Quotes turn wildcards off. echo "*.txt" prints *.txt. You need this when you want to hand the pattern itself to a command, as with find in Searching, Counting, and Sorting.
  • * doesn’t match hidden files, whose names start with a dot.
Preview before you delete

rm *.tmp deletes every matching file instantly, with no Trash. Run ls *.tmp first to see exactly what matches. Watch out for stray spaces: rm * .tmp means “delete everything, and also a file named .tmp.” (zsh on a Mac asks “sure you want to delete all … files?” before running a command like that; Bash does not ask.)

22.2 Pipes: connecting commands

A pipe, the | character, sends the output of one command into the next command as its input, like an assembly line. For example, sort -r sorts lines in reverse order, so this lists the music folder backwards:

ls music | sort -r

Output:

song2.mp3
song1.mp3

More pipelines you’ll use:

Pipeline What it does
ls -l | less shows a long file listing one screen at a time
history | grep cd shows only the past commands that contain cd
cat data.csv | wc -l counts the lines in data.csv

Compare pipes with redirection: > sends output into a file, while | sends it into another command. The next page, Searching, Counting, and Sorting, introduces grep, wc and sort, the commands you’ll most often put at the end of a pipe.

Practice Questions

Easy

Question 1 · Multiple choice

Which file names does the pattern *.csv match?

  1. Only a file literally named *.csv
  2. Every name that ends in .csv
  3. Every name that contains exactly one character before .csv
  4. Every file in the folder

b. * matches any number of characters, so *.csv matches data.csv, sales-2026.csv, and so on. Option a is what happens when the pattern is in quotes ("*.csv"), c describes ?.csv, and d describes * on its own.

Question 2 · Short answer

What does the wildcard ? match? Give an example.

Exactly one character. For example, song?.mp3 matches song1.mp3 and song2.mp3, but not song10.mp3 (two characters) or song.mp3 (none).

Question 3 · True or false

When you type ls *.txt, the ls command itself searches the folder for names that match *.txt.

False. The shell expands *.txt into the list of matching file names before running ls. ls only receives the names; it never sees the *.

Question 4 · Short answer

What does the pipe character | do in history | grep cd?

It sends the output of the first command (history, the list of past commands) into the second command (grep cd) as its input. The result shows only the past commands that contain cd.

Medium

Question 5 · Write the command

Write a command that lists only the .csv files in the current folder.

ls *.csv

Question 6 · Predict the output

In bash-tutorial-files, what do these commands print?

echo pictures/*.jpg
echo music/song?.mp3
echo "music/*.mp3"
pictures/family.jpg pictures/vacation.jpg
music/song1.mp3 music/song2.mp3
music/*.mp3

The quotes in the last command turn off wildcard expansion.

Question 7 · Write the pattern

A folder contains song1.mp3, song2.mp3, and song3.mp3. Write an ls command whose pattern matches song1.mp3 and song2.mp3, but not song3.mp3.

ls song[12].mp3

[12] matches exactly one character that is either 1 or 2. (song?.mp3 would also match song3.mp3.)

Question 8 · Predict the output

A folder contains no PDF files. What happens when you run ls *.pdf on a Mac (zsh), and in Git Bash?

  • Mac (zsh): zsh: no matches found: *.pdf. zsh stops and doesn’t run ls at all.
  • Git Bash: Bash passes the unmatched pattern unchanged, so ls looks for a file literally named *.pdf and reports ls: cannot access '*.pdf': No such file or directory.

Hard

Question 9 · Find the bug

Sam wanted to delete his backup files and typed rm * .bak (with a space before .bak). What does this command do in Git Bash? What did he mean to type, and what should he have done first?

Because of the space, * and .bak are two separate words. * matches every (non-hidden) file in the folder, so rm deletes them all, and then complains that there is no file named .bak. There is no undo. He meant rm *.bak. He should first have run ls *.bak to preview exactly which files match. (zsh on a Mac would have asked for confirmation because of the lone *, but Bash does not.)

Question 10 · Find the bug

Maria wants to read a long file listing one screen at a time, but types ls -l > less instead of ls -l | less. Nothing appears on the screen. What happened, and how can she clean up?

> is redirection, not a pipe: it sent the listing into a file named less in the current folder (creating or overwriting it) instead of into the less command. That’s why nothing was shown. She can look at the file with cat less and then delete it with rm less. The right command is ls -l | less.

Question 11 · Scenario

A project folder contains app.js, notes.txt and a hidden file .env. Sam runs echo * to see what rm * would delete. What does echo * print, and does this mean rm * is harmless to .env? Why might Sam still not want to run rm *?

It prints app.js notes.txt. * doesn’t match hidden files (names that start with a dot), so rm * would not delete .env. But it would still delete every other file in the folder instantly, with no Trash. The preview shows exactly what would be lost; if that’s not what Sam wants, use a narrower pattern such as rm *.tmp.

© 2025-2026 Y. Rosenthal. All rights reserved.