The Final Zsh Lesson: Advanced Globs, Expansion & ZLE — Tutorial #30
Video: The Final Zsh Lesson: Advanced Globs, Expansion & ZLE — Tutorial #30 by Taught by Celeste AI - AI Coding Coach
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 withvcs_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
.zshrcfrom 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.