Qualifiers, modifiers and extended globs

Zsh · Advanced · 12 min

These replace find, basename and dirname in one-liners, and work on variables too.

Glob qualifiers combine freely inside one pair of parentheses:

  • size L: (Lk+100) larger than 100 KB, (Lm-1) smaller than 1 MB,
  • time m (modified), a (accessed), c (changed): (mh-1) within the last hour, (md+30) more than 30 days ago,
  • owner: (U) owned by you, (u:root:) by root; (D) includes dotfiles,
  • sorting with o (ascending) or O (descending) followed by n name, L size, m mtime; then a subscript like [1,5],
  • ^ negates the next qualifier, , means OR between groups, and (e:'expr':) or (+func) run arbitrary tests.

Modifiers transform each match and also work on variables: :t tail (basename), :h head (dirname), :r root (drop the extension), :e extension, :a absolute path, :u and :l change case. $f:t or ${f:r} work on any variable.

With setopt EXTENDED_GLOB you get pattern operators: ^*.log everything not matching, pat~exclude match then exclude, # and ## for repetition, and (#i) for case-insensitive matching.

Zsh also has short loop forms (for f in *.txt; mv ... without do/done), which are handy at the prompt; use full syntax in scripts. For renaming many files, the bundled zmv function (autoload -Uz zmv) is safer than a loop.

Example

cd "$(mktemp -d)"
mkdir logs && print x > a.txt && head -c 200000 /dev/zero > big.bin
touch -d '40 days ago' logs/old.log && touch logs/new.log
print -l **/*(.Lk+100)          # big.bin         (larger than 100 KB)
print -l **/*.log(.md+30)       # logs/old.log    (modified > 30 days ago)
print -l *(.oL)                 # a.txt big.bin   (by size, smallest first)
f=/srv/app/report.final.csv
print $f:t $f:h                 # report.final.csv /srv/app
print $f:r $f:e                 # /srv/app/report.final csv
print -l logs/*(.:t:r)          # new old   (modifiers inside qualifiers)
setopt EXTENDED_GLOB
print -l *~*.bin                # a.txt logs    (~ excludes)
print -l **/^*.log(.)           # a.txt big.bin (^ negates the pattern)
for f in *.txt; mv -- $f $f:r.md      # short loop form: rename .txt -> .md
print -l *(.)                   # a.md big.bin

Remember

  • L size, m/a/c time, U/u owner, o/O sort, [n,m] range
  • :t :h :r :e modifiers work on matches and on variables
  • EXTENDED_GLOB adds ^ negation and ~ exclusion

Watch out for

  • Forgetting the (.) qualifier, so directories are included in a destructive command.

Check yourself

  1. Which glob lists regular files larger than 1 MB, recursively?
  2. With f=photos/cat.jpeg, what does $f:r give?

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

Builds on

Unlocks

Nothing builds on this yet.