| name | zsh-expert |
| description | Zsh scripting expert — explain Zsh-specific syntax, parameter expansions, array operations, and idioms in detail |
| user-invocable | true |
| argument-hint | <Zsh syntax or question> |
| allowed-tools | Read, Grep, Glob, WebSearch, WebFetch |
Zsh Scripting Expert
Reference skill for Zsh syntax and idioms.
Provides accurate, practical explanations when the user asks about Zsh-specific constructs.
Trigger
Automatically activate when:
- The user asks about Zsh parameter expansions, array operations, glob qualifiers, or other Zsh-specific syntax
- Code containing Zsh-specific constructs like
${(...)} or (( )) is being discussed
- Questions like "How do I ... in Zsh" or "What does this Zsh syntax do"
- Questions about differences between Bash and Zsh
Response Style
- Respond in the same language the user uses (English or Japanese)
- Start with a concise 1-2 line explanation of the syntax, then provide concrete examples
- Highlight differences from Bash when relevant
- Reference real examples from the zplug codebase with file paths and line numbers when applicable
Reference: Zsh Parameter Expansion
Basic Forms
| Syntax | Meaning |
|---|
${var} | Variable expansion |
${var:-default} | Use default if unset or empty |
${var:=default} | Assign default if unset or empty, then expand |
${var:+alt} | Use alt if set and non-empty |
${var:?message} | Error if unset or empty |
${var-default} | Use default if unset (empty is OK) |
${+var} | 1 if set, 0 if unset |
String Manipulation
| Syntax | Meaning |
|---|
${var#pattern} | Remove shortest match from beginning |
${var##pattern} | Remove longest match from beginning |
${var%pattern} | Remove shortest match from end |
${var%%pattern} | Remove longest match from end |
${var/pat/rep} | Replace first match |
${var//pat/rep} | Replace all matches |
${var/#pat/rep} | Replace match at beginning |
${var/%pat/rep} | Replace match at end |
${#var} | String length (or element count for arrays) |
${var:offset} | Substring from offset |
${var:offset:length} | Substring from offset with length |
Parameter Expansion Flags (Zsh-specific)
Used as ${(flags)var}. Multiple flags can be combined.
| Flag | Meaning | Example |
|---|
(U) | Convert to uppercase | ${(U)var} |
(L) | Convert to lowercase | ${(L)var} |
(C) | Capitalize each word | ${(C)var} |
(s:sep:) | Split on sep into array | ${(s:.:)version} → (1 0 3) |
(j:sep:) | Join array with sep | ${(j:,:)array} → a,b,c |
(f) | Split on newlines (same as (s:\n:)) | ${(f)output} |
(F) | Join with newlines (same as (j:\n:)) | ${(F)array} |
(u) | Remove duplicates (unique) | ${(u)array} |
(o) | Sort ascending | ${(o)array} |
(O) | Sort descending | ${(O)array} |
(on) | Sort numerically ascending | ${(on)array} |
(M) | Keep only matching elements | ${(M)array:#pattern} |
(k) | Get associative array keys | ${(k)assoc} |
(v) | Get associative array values | ${(v)assoc} |
(kv) | Get keys and values interleaved | ${(kv)assoc} |
(t) | Return variable type | ${(t)var} → scalar |
(P) | Indirect reference (name from value) | ${(P)name} |
(e) | Expand $ variables in value | ${(e)template} |
(q) | Add quoting | ${(q)var} |
(Q) | Remove quoting | ${(Q)var} |
(w) | Word count (with ${#}) | ${(w)#var} |
(S) | Reverse shortest/longest match for #/% | Substring match mode |
(V) | Make invisible characters visible | ${(V)var} |
(z) | Tokenize using shell word splitting | ${(z)cmdline} |
Array Filtering
| Syntax | Meaning |
|---|
${array:#pattern} | Keep elements NOT matching pattern |
${(M)array:#pattern} | Keep ONLY elements matching pattern |
${array[(i)value]} | Index of first occurrence of value |
${array[(I)value]} | Index of last occurrence of value |
${array[(r)pattern]} | First element matching pattern |
${array[(R)pattern]} | Last element matching pattern |
Reference: Array Operations
Basics
typeset -a arr=(one two three)
echo $arr[1]
echo $arr[-1]
echo $arr[2,3]
echo $arr[2,-1]
echo $#arr
echo ${#arr[@]}
arr+=("four")
arr[5]="five"
arr[2]=()
for x in "${arr[@]}"; do echo $x; done
for i in {1..$#arr}; do echo "$i: $arr[$i]"; done
Associative Arrays
typeset -A hash
hash=(key1 val1 key2 val2)
hash[key1]=val1
echo $hash[key1]
echo ${(k)hash}
echo ${(v)hash}
echo ${(kv)hash}
(( ${+hash[key1]} )) && echo "exists"
echo ${#hash}
Reference: Globbing
Extended Glob (setopt EXTENDED_GLOB)
| Pattern | Meaning |
|---|
**/ | Recursive directory match |
(pat1|pat2) | OR match |
^pattern | Negation match |
pat~exclude | Match pat but not exclude |
# | Zero or more repetitions (regex *) |
## | One or more repetitions (regex +) |
Glob Qualifiers
Used as pattern(qualifier). Filter files by attributes.
| Qualifier | Meaning |
|---|
. | Regular files only |
/ | Directories only |
@ | Symbolic links only |
* | Executable files only |
r / w / x | Owner read/write/execute |
R / W / X | World read/write/execute |
N | NULL_GLOB (no error if no match) |
D | DOTGLOB (include hidden files) |
on | Sort by name |
om | Sort by modification time |
oL | Sort by file size |
Om | Reverse sort by modification time |
[1] | First match only |
[1,5] | First 5 matches |
m-7 | Modified within 7 days |
L+1M | Larger than 1MB |
u:user: | Owned by specified user |
print -l **/*.zsh(.om)
print -l *(m-7.)
Reference: Conditionals & Arithmetic
[[ ]] Conditional Expressions
[[ -n $var ]]
[[ -z $var ]]
[[ $a == $b ]]
[[ $a != $b ]]
[[ $a =~ regex ]]
[[ $a == pattern ]]
[[ -e $file ]]
[[ -d $dir ]]
[[ -f $file ]]
[[ -L $file ]]
[[ -x $file ]]
Zsh-specific: == supports glob pattern matching inside [[ ]]. PCRE is available with =~ when setopt RE_MATCH_PCRE is set.
(( )) Arithmetic Expressions
(( x = 5 + 3 ))
(( x++ ))
(( x > 0 )) && echo positive
(( result = a > b ? a : b ))
Reference: Zsh-Specific Builtins
| Command | Meaning |
|---|
typeset / declare | Variable declaration (-a array, -A assoc array, -i integer, -r readonly, -g global) |
local | Function-local variable (equivalent to typeset) |
autoload | Declare lazy-loaded function |
autoload -Uz func | -U suppress alias expansion, -z zsh-style |
zmodload | Load Zsh modules |
zstyle | Context-based configuration |
compdef | Bind completion function |
add-zsh-hook | Add hook function to precmd/preexec etc. |
emulate -L zsh | Force Zsh emulation within a function |
setopt / unsetopt | Set/unset shell options |
print | Enhanced echo (-P prompt expansion, -l one per line, -r no escapes) |
read | -q Y/n prompt, -s silent input, -A read into array |
Reference: Useful Options
| Option | Meaning |
|---|
LOCAL_OPTIONS | Restore options set within a function on return |
LOCAL_TRAPS | Restore traps set within a function on return |
EXTENDED_GLOB | Enable extended globbing |
NULL_GLOB | No error when glob has no match |
KSH_ARRAYS | Make arrays 0-indexed (rarely used) |
WARN_CREATE_GLOBAL | Warn on implicit global variable creation in functions |
ERR_EXIT | Exit immediately on command failure |
PIPE_FAIL | Detect failures within pipelines |
NO_UNSET | Error on referencing unset variables |
Reference: Hooks & Traps
add-zsh-hook precmd my_precmd_func
add-zsh-hook preexec my_preexec_func
add-zsh-hook chpwd my_chpwd_func
add-zsh-hook zshexit my_cleanup_func
TRAPINT() { print "interrupted"; return 128+2; }
TRAPERR() { print "error: $?" >&2; }
Reference: Anonymous Functions
() {
local tmp="scoped"
echo $tmp
}
() {
echo "arg1=$1 arg2=$2"
} "hello" "world"
Reference: Key Differences from Bash
| Feature | Bash | Zsh |
|---|
| Array indexing | 0-indexed | 1-indexed |
| Array access | ${arr[0]} | $arr[1] or ${arr[1]} |
Bare $arr | First element | All elements (same as ${arr[@]}) |
| Associative array declaration | declare -A | typeset -A |
| Word splitting | On by default | Off by default |
| Glob qualifiers | None | (.om) and many more |
| Parameter expansion flags | None | ${(s:.:)var} and many more |
[[ $x == pattern ]] | Glob match | Glob match |
=~ regex | ERE (BASH_REMATCH) | ERE (MATCH, match) |
print command | Not built-in | Built-in |
emulate | Not available | Available |
| Function scoping | Dynamic | Dynamic (typeset for local) |
| Anonymous functions | Not available | () { ... } |
| Hook mechanism | Not available | add-zsh-hook |
Reference: ZLE (Zsh Line Editor)
my-widget() {
BUFFER="modified"
CURSOR=$#BUFFER
}
zle -N my-widget
bindkey '^X^M' my-widget
Answer Guidelines
- Start with a concise 1-2 line explanation of the syntax in question
- Provide concrete code examples (minimal and runnable)
- Note any gotchas or common pitfalls
- Reference real usage in the zplug codebase when applicable, using
base/path/file.zsh:NN format
- For users coming from Bash, explicitly highlight the differences
- List related syntax or flags under a "See also" section when relevant