Back to Blog

The Final Zsh Lesson: Advanced Globs, Expansion & ZLE — Tutorial #30

Sandy LaneSandy Lane

Video: The Final Zsh Lesson: Advanced Globs, Expansion & ZLE — Tutorial #30 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Zsh Lesson 30: The Finale — Advanced Globs, Expansion, ZLE

Zsh's signature features: extended globs (**/*.sh, file qualifiers *(.), *(/)), parameter expansion flags ${(U)var}, ${(j::)arr}, brace sequences {1..10}, prompt themes with vcs_info, line-editor widgets via ZLE. The features that make Zsh genuinely better than Bash. Series finale.

You've built the foundation across 29 lessons. This one's a tour of what makes Zsh special — the parts you reach for once you know they exist.

Extended globs: setopt EXTENDED_GLOB

setopt EXTENDED_GLOB

Add to ~/.zshrc. Now you have:

**/*.sh           # recursive — all .sh files in this tree
**/*.{sh,py}      # multiple extensions
*~*.bak           # everything EXCEPT .bak files
^*.log            # everything EXCEPT .log
(*.txt|*.md)     # alternation

The ^ (negation) and ~ (except) operators let you write what you mean.

File qualifiers: *(.)

After a glob, parentheses introduce qualifiers — filters by attribute:

*(.)             # regular files only
*(/)             # directories only
*(@)             # symlinks only
*(*)             # executables
*(.x)            # regular AND executable
*.{c,h}(.)       # only files (not dirs) matching {c,h}

This is a Zsh exclusive — Bash has no equivalent.

Size qualifiers

**/*(Lk+10)       # larger than 10 KB
**/*(Lk-1)        # smaller than 1 KB
**/*(LM+1)        # larger than 1 MB
*(L+0)            # non-empty

Format: L<unit><sign><N>. Unit: k KB, M MB, b bytes (default).

Time qualifiers

**/*(mh-24)       # modified in last 24 hours
**/*(m+7)         # older than 7 days (modification)
**/*(am-1)        # accessed in last minute

Units: s seconds, M minutes, h hours, d days, w weeks, M months. Sign - is "less than ago," + is "more than ago."

Sorting and limiting

**/*(.om)         # files, sorted by modification time (newest first)
**/*(.om[1,5])    # 5 newest
**/*(.OL)         # files, by SIZE (largest first; uppercase O for descending)
**/*(.oL[1,3])    # 3 smallest

Order keys: m mtime, a atime, c ctime, n name, L size. Lowercase o ascending, uppercase O descending. [start,end] for slice.

# 5 most recently modified files in this tree, regular files only
print -l **/*(.om[1,5])

Combining qualifiers

*(.x)                          # files AND executable
**/*(.LM+1.om[1,10])          # files larger than 1MB, sorted newest first, top 10
**/*.log(.m-7)                # .log files modified in last 7 days

Multiple qualifiers in one () — they all apply.

Brace expansion

echo {1..10}                  # 1 2 3 4 5 6 7 8 9 10
echo {a..f}                   # a b c d e f
echo {01..05}                 # 01 02 03 04 05 (zero-padded)
echo {1..20..4}               # 1 5 9 13 17 (step 4)
echo {z..a}                   # z y x w v u t s r q p o n m l k j i h g f e d c b a

Combine for cross-products:

echo {web,api,db}_{prod,staging}
# web_prod web_staging api_prod api_staging db_prod db_staging

Useful idioms:

cp config.json{,.bak}                    # copies to config.json.bak
mv index.{html,htm}                       # rename index.html to index.htm
mkdir -p project/{src,tests,docs}         # multiple dirs at once

Parameter expansion flags

${(flag)var} modifies the expansion:

str="hello world"
echo "${(U)str}"     # HELLO WORLD — upper
echo "${(L)str}"     # hello world — lower
echo "${(C)str}"     # Hello World — capitalize each word

arr=(c a b)
echo "${(o)arr}"     # a b c — sort
echo "${(O)arr}"     # c b a — reverse-sort
echo "${(j:,:)arr}"  # a,c,b — join (input order, just joined with comma)
echo "${(s:,:)$(echo a,b,c)}"   # split

echo "${#str}"       # length
echo "${(@)arr}"     # array form
echo "${(P)varname}" # indirect: the value of variable named in $varname

# Padding
num=42
echo "${(l:5::0:)num}"     # 00042 — left-pad to 5 with '0'
echo "${(r:10:)str}"       # right-pad to 10 with space

Hundreds of flags. The most useful: (U) (L) (C) (s:sep:) (j:sep:) (o) (O) (l:N::char:) (r:N:).

Modifiers

filename="/path/to/report.txt.gz"

echo "${filename:t}"     # report.txt.gz — tail (basename)
echo "${filename:h}"     # /path/to — head (dirname)
echo "${filename:r}"     # /path/to/report.txt — root (no extension)
echo "${filename:e}"     # gz — extension
echo "${filename:t:r:r}" # report — chained

:t :h :r :e are modifier suffixes — like file path operations without forking.

vcs_info: git status in prompt

autoload -Uz vcs_info
zstyle ':vcs_info:git:*' formats '%F{yellow}(%b)%f'
zstyle ':vcs_info:git:*' actionformats '%F{red}(%b|%a)%f'

precmd_functions+=(vcs_info)

setopt prompt_subst
PROMPT='%F{cyan}%n%f@%F{green}%m%f %F{blue}%~%f ${vcs_info_msg_0_} %# '

vcs_info queries the current directory's VCS state. formats controls the display. precmd_functions+=(vcs_info) runs it before each prompt.

Result: alice@laptop ~/work (main) %. The (main) highlights when you're in a git repo.

ZLE: the line editor

ZLE (Zsh Line Editor) is what handles your typing — moves the cursor, runs widgets, etc. You can extend it.

A custom widget that opens the editor on the current command:

# Already built into Zsh: ESC then v (in vi mode), or Ctrl-X Ctrl-E
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey '^X^E' edit-command-line

Now Ctrl-X Ctrl-E opens your $EDITOR with the current command line. Edit, save, run.

A custom widget for "insert sudo at the start":

function prefix-sudo() {
  BUFFER="sudo $BUFFER"
  CURSOR=$((CURSOR + 5))
}
zle -N prefix-sudo
bindkey '^[s' prefix-sudo    # Alt-S

Now Alt-S prefixes sudo. Useful when you forgot.

Custom completions

# Custom completion for "dev" command
_dev() {
  local -a subcommands
  subcommands=(
    'start:start the dev server'
    'stop:stop the server'
    'logs:show logs'
    'status:show status'
  )
  _describe 'subcommand' subcommands
}
compdef _dev dev

Now dev <Tab> shows the four subcommands. For complex tools, this transforms usability.

compdef registers completion for a command. _describe is the standard "show this list" helper.

Useful setopts (recap)

setopt AUTO_CD              # cd by typing directory name
setopt AUTO_PUSHD           # cd builds a stack
setopt PUSHD_IGNORE_DUPS    # no duplicates on the stack
setopt SHARE_HISTORY        # all shells see history
setopt HIST_IGNORE_DUPS
setopt HIST_REDUCE_BLANKS
setopt EXTENDED_GLOB
setopt INTERACTIVE_COMMENTS # # at prompt is a comment
setopt PROMPT_SUBST
setopt CORRECT              # propose corrections for typos
setopt NO_BEEP

Add to ~/.zshrc. See man zshoptions for the full list (300+).

A polished prompt

# ~/.zshrc

# Plugins / completions
autoload -Uz compinit && compinit
autoload -Uz vcs_info

# Options
setopt AUTO_CD AUTO_PUSHD EXTENDED_GLOB SHARE_HISTORY HIST_IGNORE_DUPS PROMPT_SUBST

# History
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000

# vcs_info config
zstyle ':vcs_info:git:*' formats '%F{yellow} (%b)%f'
zstyle ':vcs_info:git:*' actionformats '%F{red} (%b|%a)%f'
precmd_functions+=(vcs_info)

# Prompt
PROMPT='%F{cyan}%n%f@%F{green}%m%f %F{blue}%~%f${vcs_info_msg_0_} %# '
RPROMPT='%F{240}%*%f'    # current time, dim, on the right

# Aliases
alias ll='ls -lah'
alias gs='git status -s'
alias ..='cd ..'

# PATH
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"

A real, complete .zshrc you could ship to a friend.

A prompt one-liner with everything

PROMPT='%(?.%F{green}.%F{red})%n%f@%F{cyan}%m%f:%F{blue}%~%f${vcs_info_msg_0_}
%(?..%F{red}[%?]%f )%# '

%(?.green.red) colors username by exit status. Multi-line. [%?] shows last exit code in red on failure.

A prompt you'll keep refining for years.

The series ends here

Across 30 lessons, we've covered:

  • Foundations — first commands, navigation, files, viewing, customization, scripting.
  • Scripting — variables, input, conditionals, loops, functions, arrays, strings, file I/O, processes.
  • Pipes & tools — pipes/redirection, grep, sed, awk, error handling, find, jq, curl, ssh.
  • Automation — cron/launchd, Oh My Zsh, building CLIs, backup/deploy/maintenance, macOS tricks, git workflows.
  • Advanced — globs, qualifiers, parameter expansion, vcs_info, ZLE, completions.

Enough to write production scripts, build CLIs, automate your machine, and feel at home in the terminal.

Next steps

Pick a project. Some ideas:

  • A personal CLI suite. Tasks you do daily as terminal commands.
  • Replace your dotfiles framework. Write a minimal .zshrc from scratch — drop OMZ, just what you use.
  • A deployment script. For a real project. Build, deploy, verify, rollback.
  • A monitoring dashboard. top-style live view of something you care about (your services, your weather, your todo list).
  • Contribute to a CLI tool. Many on GitHub. The maintainers always need help.

The shell rewards investment. Every hour spent learning saves ten hours of dragging the mouse.

Common stumbles (final)

setopt EXTENDED_GLOB not set. Many examples in this lesson assume it. Add to ~/.zshrc.

Bash habits. Index 0, no ${(...)} flags, $arr doesn't expand. Zsh is similar but not identical.

Slow shell startup. Profile with zprof. Lose plugins you don't use.

Configurations that "almost work." When a snippet from the internet errors, read carefully — paste-and-pray is debugging hell. Type each line; understand each line.

Not committing dotfiles. Set up a private git repo for ~/.zshrc, ~/.gitconfig, ~/.ssh/config, etc. New machine setup becomes git clone.

Recap

setopt EXTENDED_GLOB for **, ^, ~. File qualifiers *(.), *(/), *(@). Size *(LM+1), time *(m-7), sort *(.om[1,5]). Brace expansion {1..10}, {a,b}_{x,y}. Parameter flags ${(U)var}, ${(j:,:)arr}, ${(s:,:)str}, padding ${(l:N::pad:)var}. Modifiers :t :h :r :e. vcs_info for git in prompt. compdef for custom completions. ZLE widgets via zle -N + bindkey. Build a .zshrc you maintain like real code.

Series complete. Now go build something with Zsh — the only way to truly learn it.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.