Zsh’s extended globbing is a powerful feature that lets you match filenames in ways that go far beyond the basic wildcards you might be used to.

Let’s see it in action. Imagine you have a directory with files like:

report_2023_01.txt
report_2023_02.txt
report_2024_01.txt
report_2024_02.txt
data_2023_01.csv
data_2024_01.csv

With standard globbing, ls report_*.txt would give you all the .txt reports. But what if you only want the reports from 2023?

Here’s how extended globbing shines. First, make sure it’s enabled in your Zsh session. You can do this with setopt EXTENDED_GLOB.

Now, to list only the .txt reports from 2023, you’d use:

ls report_2023_*.txt

This is straightforward. The real power comes with more complex patterns. Let’s say you want to match either .txt files or .csv files, but only those starting with report_.

You can use the | operator for alternation:

ls report_*.txt(.) # Match only regular files
ls report_*.csv(.)

Combining them, you might think of something like ls report_*.(txt|csv). This works!

ls report_*.(txt|csv)

This command will list report_2023_01.txt, report_2023_02.txt, report_2024_01.txt, and report_2024_02.txt. Notice that data_*.csv files are not included because the pattern starts with report_.

Extended globbing also offers powerful exclusion and repetition operators.

The ^ operator negates a pattern. If you wanted all .txt files except those from 2024, you could try:

ls report_*.txt^2024*

This would list report_2023_01.txt and report_2023_02.txt.

The (pattern) syntax is for grouping alternatives. The * after it means "zero or more of the preceding group." So, report_*.(txt|csv) means "report_", followed by zero or more of anything, followed by ".txt" or ".csv".

Consider the ** globstar. This is not strictly part of extended globbing but works beautifully with it. ** recursively matches directories. So, ls **/*.txt would list all .txt files in the current directory and any subdirectories.

Combining ** with extended globbing: imagine you want to find all .txt files in any subdirectory, but only those whose names contain the digit '2'.

ls **/*2*.txt

This command would find files like projectA/logs/error_log_202311.txt and projectB/archive/backup_2022_01.txt.

The (#N) pattern can be used for repetition counts. For example, to match filenames with exactly three digits after report_:

ls report_(#3D).txt

Where D represents a digit. This would match report_123.txt but not report_45.txt or report_6789.txt.

One of the most surprising aspects is how these globbing patterns can be combined with Zsh’s powerful glob qualifiers. You’ve already seen (.) which matches only regular files. Other useful qualifiers include (/) for directories, (*) for executables, and (@) for symbolic links.

For instance, to find all directories named archive or backup anywhere in the current directory tree:

ls **/(archive|backup)/

This uses ** for recursion, () for grouping the directory names, and / as a glob qualifier to ensure we only match directories.

The true power emerges when you chain these features. Suppose you want to find all configuration files (.conf or .cfg) in any subdirectory, but exclude any that are symbolic links.

ls **/*.(conf|cfg)(^@)

This command first recursively searches (**) for files ending in .conf or .cfg (*.(conf|cfg)), and then the (^@) qualifier excludes any results that are symbolic links.

The way Zsh parses these patterns is quite sophisticated. When you use setopt EXTENDED_GLOB, it enables a richer set of matching rules. The core idea is to move from simple character matching to pattern composition. Alternation (|), grouping (()), negation (^), and repetition ((#N)) allow you to build highly specific file selection logic directly on the command line.

The (#) construct is particularly flexible for specifying counts. (#N) means exactly N repetitions, (#N,) means at least N, (#,N) means at most N, and (#N,M) means between N and M repetitions. This allows for precise matching of numerical sequences or fixed-length strings within filenames.

If you’ve been using find with grep for complex file searches, Zsh’s extended globbing can often replace those combinations with a single, more readable command. It’s a fundamental tool for efficient shell scripting and interactive use.

The next logical step is to explore Zsh’s powerful array manipulation and how globbing results can be directly assigned to arrays for further processing.

Want structured learning?

Take the full Zsh course →