Zsh Glob Qualifiers: Filter Files At Match Time
Zsh glob qualifiers let you filter files by attributes like size and mtime directly in the pattern, reducing post-processing and keeping scripts lean.
16 Oct 2026, 15:42 UTC

The post-filtering habit costs you
Typical shell scripts expand * first, then filter. That means the shell builds a list of every entry in the directory, passes it to a command, and that command discards most of it. On a directory with thousands of files, the expansion and the subsequent pipeline are wasted work, and the overhead grows quickly as the filesystem scales.
How qualifiers read
A qualifier lives in parentheses immediately after the glob, with an optional leading . to restrict to regular files. Inside you chain attribute tests. Zsh evaluates qualifiers lazily: as the glob expands, each candidate is tested, and non-matching names are discarded before they ever enter a list, so the shell never builds a full inventory of the directory. This lazy evaluation means qualifiers can be more efficient than piping a full expansion to find or grep.
-mtime +7modified more than 7 days ago,-mtime -1modified within the last day.-size +1mlarger than 1 megabyte. Units: b, k, m, g.-type fregular file,-type lsymlink. Short forms(+h)for symlink and(-h)for not symlink also exist.(N)nullglob: return empty instead of the literal pattern when nothing matches.
Worked example: collect old logs for archiving
Run this in a Zsh session or a script started with #!/usr/bin/env zsh. It requires read permission on the target directory and write permission only if you later move or compress files. Build an array of qualifying logs with a single pattern: logs=($LOGDIR/*.log(N . -type f -size +1m -mtime +7)). The (N) qualifier suppresses errors if no files match, and the qualifier chain filters by type, size, and mtime at match time. Inspect the count with echo ${#logs} to verify only the intended files were kept.
What to check:
- Run
echo $LOGDIR/*.log(N)first to confirm the pattern expands at all. - Compare
echo ${#logs}with a manuallscount to verify the qualifier filtered correctly. - If you expect zero matches, the
(N)qualifier prevents the literal pattern from being passed to the loop.
Trade-offs and limits
Qualifiers are Zsh-specific. A script using *(+h) or -mtime qualifiers will not run under Bash or POSIX sh without rewriting the file-selection logic. If you need portability, keep qualifier logic inside Zsh-only helper functions and fall back to find elsewhere. Readability drops quickly with long chains. *.tmp(-mtime +30 -size +10m -type f) is clear, but adding owner, permissions, and multiple negations can become hard to review. Prefer extracting complex filters into a named function or a comment that explains the intent. Performance benefit is real for large directories because only matching names are returned, but the benefit disappears if you immediately store all matches in an array and then process them one by one. For one-off checks, printing directly is lighter.
Verification tip: use time around two equivalent forms on a test directory with many files. The qualifier version should avoid building the full list, though the exact saving depends on filesystem and load.
Actionable closing
Use qualifiers when you need a precise file set inside Zsh and you control the execution environment. Start with a simple pattern and one attribute, verify the count with ${#array}, then add constraints. Document the qualifier intent in a comment, and isolate Zsh-only code so portability remains an option later.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.