🐚 Zsh Differences Reference
Zsh (Z Shell) extends the Bourne shell with numerous features while maintaining compatibility. Understanding zsh-specific capabilities helps leverage its power while avoiding portability issues.
🎯 Major Zsh Features
Extended Globbing
Zsh provides powerful pattern matching beyond standard shell globbing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | # Enable extended globbing
setopt extended_glob
# Recursive globbing
ls **/*.txt # Find all .txt files recursively
# Negation patterns
ls ^*.log # All files except .log files
ls *.txt~backup.* # .txt files except those starting with backup
# Pattern exclusions
ls (*.txt|^backup*) # .txt files but not starting with backup
# Approximate matching
ls (#a1)file.txt # Files similar to file.txt (1 character difference)
# Numeric ranges
ls file<1-10>.txt # file1.txt through file10.txt
# Qualifiers (file properties)
ls *(.) # Regular files only
ls *(/) # Directories only
ls *(@) # Symlinks only
ls *(rwx) # Files with specific permissions
ls *(m-1) # Files modified within last day
ls *(^m+7) # Files not modified in last 7 days
|
Arrays and Subscripts
Zsh offers advanced array handling with flexible indexing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | # Array creation and manipulation
fruits=(apple banana cherry)
fruits+=("date") # Append element
fruits[5]="elderberry" # Sparse array
# Array slicing
echo ${fruits[1,2]} # First two elements
echo ${fruits[2,-1]} # From second to last
echo ${fruits[@]:1:2} # POSIX-style slicing
# Array operations
echo ${#fruits} # Array length
echo ${fruits[(i)banana]} # Index of element
echo ${fruits[(r)b*]} # Element matching pattern
# Reverse array
echo ${fruits[::-1]}
# Unique elements
unique_fruits=(${(u)fruits})
# Sorted array
sorted_fruits=(${(o)fruits})
|
🔧 Parameter Expansion Extensions
Advanced Parameter Substitution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | # Case conversion
name="John Doe"
echo ${name:l} # john doe (lowercase)
echo ${name:u} # JOHN DOE (uppercase)
# Capitalization
echo ${name:c} # John doe (capitalize first)
# String manipulation
text="hello world"
echo ${text:s/world/universe/} # hello universe (substitute)
# Length and substrings
echo ${#text} # 11 (length)
echo ${text:6:5} # world (substring)
# Split and join
csv="a,b,c"
array=(${(s:,:)csv}) # Split on comma
joined=${(j:,:)"$array"} # Join with comma
# Remove duplicates
duplicated=(a b c a b)
unique=(${(u)duplicated}) # a b c
# Sort arrays
unsorted=(zebra alpha beta)
sorted=(${(o)unsorted}) # alpha beta zebra
reverse_sorted=(${(O)unsorted}) # zebra beta alpha
|
Parameter Flags
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | # Modifiers for parameter expansion
file="/home/user/documents/file.txt"
echo ${file:a} # Absolute path
echo ${file:A} # Resolved absolute path
echo ${file:h} # Head (directory) /home/user/documents
echo ${file:t} # Tail (filename) file.txt
echo ${file:r} # Root (without extension) /home/user/documents/file
echo ${file:e} # Extension txt
# Combining modifiers
echo ${file:h:t} # documents (parent directory name)
echo ${file:t:r} # file (basename without extension)
# Pattern-based modifications
path="/usr/local/bin/script.sh"
echo ${path:s/local/global/} # /usr/global/bin/script.sh
|
🎨 Programming Constructs
Anonymous Functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | # Anonymous functions with automatic cleanup
(){ echo "Temporary function"; } always { echo "Cleanup code"; }
# Function with try/catch-like behavior
try_this() {
# Main code
} always {
# Cleanup code (always executes)
echo "Cleaning up..."
}
# Exception-like handling
might_fail() {
if (( RANDOM % 2 )); then
return 1
fi
echo "Success"
} always {
if [[ $? -ne 0 ]]; then
echo "Function failed, cleaning up..."
fi
}
|
Mathematical Functions
1
2
3
4
5
6
7
8
9
10
11
12 | # Advanced mathematical operations
result=$(( sin(1.5) + cos(0.5) )) # Trigonometric functions
result=$(( 2**10 )) # Exponentiation
result=$(( 10#0xFF )) # Hexadecimal to decimal
# Floating point arithmetic
zmodload zsh/mathfunc
result=$(echo "sqrt(16)" | bc -l) # Or use built-in math functions
# Complex number support
zmodload zsh/curses
# (Advanced mathematical operations)
|
🔍 Completion System
Programmable Completion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | # Advanced completion with descriptions
_my_command() {
local -a commands
commands=(
'start:start the service'
'stop:stop the service'
'restart:restart the service'
'status:show service status'
)
_describe 'command' commands
}
compdef _my_command my_command
# File completion with patterns
_my_tool() {
_arguments \
'(-f --file)'{-f,--file}'[input file]:file:_files -g "*.txt"'
}
# Dynamic completion
_kubectl_resources() {
local resources
resources=($(kubectl api-resources -o name 2>/dev/null))
_describe 'resource' resources
}
|
📊 Advanced I/O Features
Named Directories
| # Named directory shortcuts
hash -d proj=/home/user/projects
cd ~proj/myproject # Go to /home/user/projects/myproject
# Automatic named directories
cd /very/long/path/to/project # Automatically creates named directory
cd ~project # Later access
# List named directories
hash -d
|
Socket and Network Features
| # TCP connections as file descriptors
ztcp hostname port
exec {fd}<>/dev/tcp/hostname/port
print -u$fd "GET / HTTP/1.0"
reply=$(<&$fd)
exec {fd}>&-
# UDP connections
ztcp -u hostname port
|
🛠️ Zsh-Specific Builtins
Enhanced Builtins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | # zcompile - compile zsh scripts for faster loading
zcompile ~/.zshrc
# zprof - profile shell performance
zmodload zsh/zprof
# ... run commands ...
zprof
# zpty - pseudo-terminal handling
zmodload zsh/zpty
zpty mysession ssh server
zpty -r mysession response
zpty -w mysession "command"
# zsh/net/tcp - network programming
zmodload zsh/net/tcp
ztcp server.com 80
|
Option Management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | # Powerful option handling
setopt autocd autopushd pushdminus
unsetopt beep nomatch
# List all options
setopt
# Check specific options
if [[ -o autocd ]]; then
echo "autocd is enabled"
fi
# Useful zsh options
setopt \
extended_glob \ # Extended pattern matching
glob_dots \ # Match dotfiles without ./
inc_append_history \ # Append to history immediately
share_history \ # Share history between sessions
hist_ignore_dups \ # Ignore consecutive duplicates
hist_reduce_blanks \ # Remove extra blanks from history
correct \ # Spelling correction
auto_cd \ # cd without typing cd
auto_pushd \ # Push directories to stack automatically
|
🔄 Process Management
Job Control Extensions
| # Enhanced job control
jobs -l # List jobs with PIDs
jobs -r # Running jobs only
jobs -s # Stopped jobs only
# Job qualifiers
fg %?pattern # Foreground by pattern matching
bg %string # Background by string in command
# Process substitution with advanced features
diff =(sort file1) =(sort file2) # Process substitution
|
Coprocesses
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # Advanced coprocess management
coproc sort_filter {
while read line; do
if [[ $line =~ ^[0-9]+$ ]]; then
echo "NUMBER: $line"
else
echo "TEXT: $line"
fi
done
}
# Send data to coprocess
print -p "123"
print -p "hello"
# Read responses
read -p response
echo "Got: $response"
|
🎨 Prompt Customization
Advanced Prompt Escapes
1
2
3
4
5
6
7
8
9
10
11
12
13 | # Rich prompt customization
PROMPT='%F{blue}%n%f@%F{green}%m%f:%F{yellow}%~%f %# '
# Prompt with git branch
autoload -Uz vcs_info
zstyle ':vcs_info:*' formats '%b'
precmd() { vcs_info }
RPROMPT='${vcs_info_msg_0_}'
# Asynchronous prompt updates
async_init
async_start_worker myworker -n
async_register_callback myworker prompt_callback
|
Right-side Prompts
| # Right-aligned prompt elements
RPROMPT='%F{red}%D{%H:%M:%S}%f'
# Multiple prompt lines
PROMPT='%F{blue}%~%f
%# '
# Conditional prompts
PROMPT='%(?..%F{red}%?%f )%# ' # Show exit code if non-zero
|
⚠️ Zsh-Specific Features to Avoid for Portability
Non-Portable Constructs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # ❌ Zsh-specific features
array=(item1 item2 item3) # Use in portable scripts
echo ${array[1]} # 1-indexed arrays
echo ${#array} # Array length syntax
echo ${array:#pattern} # Array filtering
# ✅ Portable alternatives
# Use delimited strings:
ITEMS="item1:item2:item3"
IFS=':' read -ra items <<< "$ITEMS"
echo "${items[0]}" # 0-indexed in bash
echo "${#items[@]}" # Portable array length
# ❌ Extended globbing
ls **/*.txt # Not POSIX
ls ^*.log # Negation patterns
# ✅ Portable alternatives
find . -name "*.txt" # Recursive find
ls | grep -v '\.log$' # Pattern exclusion
|
🧾 Version-Specific Features
Recent Zsh Features
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # Zsh 5.0+ features
# Named references (similar to bash nameref)
typeset -n ref=original_var
ref="new value" # Modifies original_var
# Improved parameter expansion
echo ${array[(b:2:i)pattern]} # Backward search from index 2
# Enhanced completion matching
zstyle ':completion:*' matcher-list \
'' 'm:{a-z\-}={A-Z\_}' 'r:[^[:alpha:]]||[[:alpha:]]=** r:|=* m:{a-z\-}={A-Z\_}' 'r:|?=** m:{a-z\-}={A-Z\_}'
# Multibyte character support
# Better Unicode handling in prompts and completions
|
Configuration Management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # Zsh configuration files loading order
# 1. /etc/zshenv
# 2. ~/.zshenv
# 3. /etc/zprofile (login shells)
# 4. ~/.zprofile (login shells)
# 5. /etc/zshrc (interactive shells)
# 6. ~/.zshrc (interactive shells)
# 7. /etc/zlogin (login shells)
# 8. ~/.zlogin (login shells)
# Conditional configuration
if [[ -o interactive ]]; then
# Interactive shell settings
bindkey -e # Emacs key bindings
fi
if [[ -o login ]]; then
# Login shell settings
source ~/.profile
fi
|
🧾 Summary Comparison
Feature Matrix
| Feature |
Zsh |
Bash |
POSIX sh |
ksh |
| Arrays |
✅ Advanced |
✅ Basic |
❌ |
✅ Basic |
| Extended Globbing |
✅ Rich |
Limited |
❌ |
Limited |
| Parameter Expansion |
✅ Extensive |
Good |
Basic |
Good |
| Completion System |
✅ Advanced |
Good |
❌ |
Basic |
| Anonymous Functions |
✅ |
❌ |
❌ |
❌ |
| Named Directories |
✅ |
❌ |
❌ |
❌ |
| Right Prompts |
✅ |
❌ |
❌ |
❌ |
| Spell Correction |
✅ |
❌ |
❌ |
❌ |
| Floating Point |
✅ (modules) |
❌ |
❌ |
❌ |
When to Use Zsh Features
✅ Use Zsh-specific features when:
- Targeting interactive shell environments
- Need advanced completion and customization
- Want powerful pattern matching capabilities
- Leveraging desktop/user environment scripts
- Building sophisticated shell applications
❌ Avoid Zsh-specific features when:
- Writing portable system scripts
- Targeting minimal environments
- Need maximum compatibility
- Following strict POSIX requirements
- Developing production automation scripts
🧠 Best Practices
Zsh Usage Guidelines
- Explicit Shell Declaration: Use
#!/usr/bin/env zsh for Zsh-specific scripts
- Feature Detection: Check for Zsh features before using them
- Module Loading: Use
zmodload for advanced features
- Option Management: Document required Zsh options
- Completion Integration: Leverage Zsh's completion system
- Performance Considerations: Use
zcompile for large scripts
- Debugging: Use
zprof for performance profiling
Feature Detection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | # Check if running in Zsh
if [ -n "$ZSH_VERSION" ]; then
echo "Running in Zsh version $ZSH_VERSION"
# Check for specific features
if [[ ${ZSH_VERSION%%.*} -ge 5 ]]; then
echo "Zsh 5.0+ features available"
fi
# Load modules safely
if zmodload zsh/complist 2>/dev/null; then
echo "Complist module loaded"
fi
fi
# Conditional feature usage
enhanced_prompt() {
if [ -n "$ZSH_VERSION" ]; then
# Zsh-specific prompt
PROMPT='%F{blue}%n%f@%F{green}%m%f:%F{yellow}%~%f %# '
else
# Basic prompt for other shells
PS1='\u@\h:\w \$ '
fi
}
|
🧾 See Also