Zsh’s globbing is so powerful it can feel like a programming language, but its true magic lies in how it lets you precisely select files without writing explicit loops.

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

report_2023_01.txt
report_2023_02.txt
report_2023_01.csv
report_2024_01.txt
data_2023_01.txt

You want to find all .txt files from January 2023. A simple ls report_2023_01.txt is too specific. ls report_*.txt is too broad, catching report_2024_01.txt.

Here’s how Zsh’s advanced globbing can nail it:

% ls report_2023_??.txt
report_2023_01.txt
report_2023_02.txt

This uses the ?? pattern to match exactly two characters. But what if we wanted files modified after a certain date, or only files owned by a specific user? This is where qualifiers come in.

Qualifiers are appended to glob patterns within square brackets [].

  • Time-based qualifiers:

    • m[date]: Modified after date (YYYY-MM-DD).
    • M[date]: Modified before date.
    • c[date]: Changed (metadata) after date.
    • C[date]: Changed before date.
    • a[date]: Accessed after date.
    • A[date]: Accessed before date.
  • Ownership/Permission qualifiers:

    • u[user]: Owned by user.
    • g[group]: Owned by group.
    • U[user]: Not owned by user.
    • G[group]: Not owned by group.
    • p[perms]: Has exact permissions perms (e.g., p644).
    • P[perms]: Does not have exact permissions.
  • Type qualifiers:

    • f: Regular file.
    • d: Directory.
    • L: Symbolic link.
    • s: Socket.
    • p: Named pipe (FIFO).
    • b: Block device.
    • c: Character device.

Let’s combine these. Find all .txt files in the current directory modified after January 15th, 2023, and owned by the user alice:

% ls *(.m[2023-01-15].u[alice])

This command will only list files that meet all these criteria: they are regular files (.), modified after 2023-01-15 (m[2023-01-15]), and owned by alice (u[alice]).

The mental model here is a filter pipeline. The glob pattern (*) is the initial input. Each qualifier is a sequential filter applied to that input. Zsh doesn’t just match filenames; it can inspect their metadata and attributes.

Flags modify the behavior of the globbing itself, rather than filtering the results. They are usually placed at the beginning of the pattern, prefixed with (-) or (/).

  • Common Flags:
    • N: NULLGLOB. If no matches are found, the pattern expands to nothing (an empty string) instead of remaining as literal text. This prevents errors in commands like rm *nonexistent_files.
    • i: IGNORECASE. Case-insensitive matching.
    • u: UNMATCHED_BEHAVIOR. If the pattern doesn’t match anything, it’s an error. This is the default for non-empty patterns. NULLGLOB overrides this.
    • k: KSH_GLOB. Enables Korn shell-style globbing, which is more restrictive.
    • R: RECURSIVE. Glob into subdirectories. **/* will match files in the current directory and all subdirectories recursively.

Consider this: you want to find all files named README (case-insensitive) anywhere in the current directory and its subdirectories.

% ls -d **/*README*(i)

The **/* part handles the recursion. README is the literal string. (i) makes it case-insensitive, so it matches README, readme, ReadMe, etc. -d is important here because ls by default lists directory contents. We want to list the directory itself if it matches, not its contents.

The truly mind-bending part is that qualifiers can be applied to the recursive ** globbing itself. For example, to find all directories modified after a specific date anywhere in your filesystem:

% ls -d **/(d.m[2023-01-01])

This asks Zsh to traverse all directories (**), and for each directory it finds, check if it’s a directory (d) and if its modification time is after 2023-01-01 (m[2023-01-01]). The ls -d ensures that if a matching directory is found, its name is printed. Without -d, ls would try to list the contents of the matching directories.

This system allows for incredibly precise file selection without writing complex scripts. You can chain multiple qualifiers to narrow down your search to exactly what you need, leveraging file metadata directly in your command line.

The next frontier is using these advanced globs within Zsh functions and scripts to automate complex file management tasks.

Want structured learning?

Take the full Zsh course →