Recursive globs and qualifiers

Zsh · Essentials · 9 min

Zsh globs replace most simple find commands with something shorter you can type at the prompt.

Zsh supports the same *, ? and [...] patterns as bash, plus a few things that make it shine at the prompt.

Recursive globbing is always on: **/*.py matches Python files in this directory and every subdirectory (bash needs shopt -s globstar).

Glob qualifiers go in parentheses at the end of a pattern and filter or sort the matches:

  • (.) regular files, (/) directories, (@) symlinks, (*) executables,
  • (om) sort by modification time, newest first; (on) by name,
  • [1] or [1,3] take the first match or a range: *(om[1]) is the newest file,
  • (N) makes this pattern expand to nothing if it matches nothing.

The biggest difference from bash: when a pattern matches nothing, zsh reports "no matches found" and does not run the command (option NOMATCH). Bash would pass the literal pattern along. This bites with URLs and arguments containing ?, * or [, such as curl https://x.io/?q=1 or pip install foo[dev]: quote them, or prefix the command with noglob.

Use print -l (one item per line) to preview what a glob expands to before using it with rm or mv.

Example

cd "$(mktemp -d)"
mkdir -p src/lib && touch src/a.py src/lib/b.py notes.txt
print -l **/*.py        # src/a.py  src/lib/b.py   (recursive, built in)
print -l *(/)           # src         (directories only)
print -l **/*(.)        # every regular file, recursively
touch -d '2 days ago' old.txt
print -l *.txt(om)      # notes.txt old.txt   (newest first)
print -l *.txt(om[1])   # notes.txt           (just the newest)
print -l *.zip(N)       # (nothing: N = null glob for this pattern)
print 'https://x.io/?q=1'   # quote URLs: ? is a glob character
ls *.zip                # zsh: no matches found: *.zip

Remember

  • ** is recursive by default
  • Qualifiers like (.) (/) (om[1]) filter and sort matches
  • No match is an error in zsh; quote URLs or use (N) / noglob

Watch out for

  • Running pip install requests[socks] unquoted and getting 'no matches found'.

Check yourself

  1. Which glob expands to the most recently modified file in the directory?
  2. `curl https://api.example.com/items?id=4` fails with 'no matches found'. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: zsh.sourceforge.io/Doc/Release/Expansion.html#Glob-Qualifiers