๐ Advanced Expansions
Shell expansions transform text and compute values inline โ mastering them enables concise, powerful scripts.
๐งญ Expansion Types Overview
Shells perform several types of expansions in order:
- Brace Expansion โ
{a,b}{1,2}
- Tilde Expansion โ
~, ~/Documents
- Parameter Expansion โ
$var, ${var}, ${var:-default}
- Command Substitution โ
$(command), `command`
- Arithmetic Expansion โ
$((expression))
- Word Splitting โ Splitting on
$IFS
- Filename Expansion โ Globbing
*.txt
๐งช Parameter Expansion Deep Dive
Default Value Substitution
| # If var is unset or empty, use default
echo "${var:-default}" # Use default
: "${EDITOR:=nano}" # Set if unset
echo "${var:?Error message}" # Exit if unset
echo "${var:+alternative}" # Use alternative if set
|
String Manipulation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | filename="/home/user/document.pdf"
# Remove shortest match from beginning
echo "${filename#/*/}" # user/document.pdf
# Remove longest match from beginning
echo "${filename##/*/}" # document.pdf
# Remove shortest match from end
echo "${filename%.*}" # /home/user/document
# Remove longest match from end
echo "${filename%%.*}" # /home/user/document
# Replace first occurrence
echo "${filename/\/home/\/opt}" # /opt/user/document.pdf
# Replace all occurrences
echo "${filename//\//_}" # _home_user_document.pdf
|
| string="Hello World"
# Extract substring
echo "${string:0:5}" # Hello
echo "${string:6}" # World
echo "${string: -5}" # World (note space before -)
# Length
echo "${#string}" # 11
|
๐ง Pattern-Based Expansions
Case Conversion (Bash 4.0+)
| str="Hello World"
echo "${str^^}" # HELLO WORLD
echo "${str,,}" # hello world
echo "${str^}" # Hello World (first char)
echo "${str,}" # hello World (first char)
|
Indirect Variable References
| varname="USER"
value="${!varname}" # Equivalent to $USER
# Dynamic configuration access
DB_HOST="localhost"
DB_PORT="5432"
for var in DB_HOST DB_PORT; do
echo "${var}: ${!var}"
done
|
โ ๏ธ Not POSIX โ avoid in portable scripts.
๐งช Command Substitution
Modern Syntax
| # Preferred method
today=$(date +%Y-%m-%d)
files=$(find . -name "*.log")
# Nested substitution
result=$(grep -l "$(hostname)" configs/*.conf)
|
Legacy Syntax (Avoid)
| # Old style - harder to nest
today=`date +%Y-%m-%d`
|
Handling Special Characters
| # Command substitution removes trailing newlines
output=$(printf "line1\nline2\n")
echo "[$output]" # [line1
# line2]
# Preserve all output
output=$(command; echo x)
output=${output%x}
|
๐ง Arithmetic Expansion
Basic Arithmetic
| # Simple calculations
sum=$((2 + 3 * 4)) # 14
index=$((index + 1))
# Bit operations
mask=$((0xFF & 0x0F)) # 15
shifted=$((8 << 2)) # 32
|
Conditional Expressions
| # Ternary-like operator
result=$(( a > b ? a : b ))
# Boolean logic
enabled=$(( flag == 1 ? 1 : 0 ))
|
Array Indexing (Bash)
| arr=(apple banana cherry)
index=1
echo "${arr[$index]}" # banana
echo "${arr[$((index + 1))]}" # cherry
|
๐งช Brace Expansion
Sequence Generation
| # Numeric ranges
echo file{1..5}.txt # file1.txt file2.txt ... file5.txt
# Zero-padded sequences
echo file{01..05}.txt # file01.txt file02.txt ... file05.txt
# Reverse sequences
echo {5..1} # 5 4 3 2 1
# Character ranges
echo {a..e} # a b c d e
|
Cartesian Products
| # Generate combinations
echo {a,b}{1,2} # a1 a2 b1 b2
# Complex structures
mkdir -p project/{src,tests,docs}/{main,util,shared}
|
๐ง Word Splitting and IFS
Default Behavior
| # Default IFS: space, tab, newline
words="one two three"
for word in $words; do
echo "Word: [$word]"
done
|
Custom IFS
1
2
3
4
5
6
7
8
9
10
11
12 | # Split on comma
data="apple,banana,cherry"
IFS=',' read -ra fruits <<< "$data"
for fruit in "${fruits[@]}"; do
echo "Fruit: [$fruit]"
done
# Temporary IFS change
old_ifs="$IFS"
IFS=':'
read user pass uid gid rest < /etc/passwd
IFS="$old_ifs"
|
Preventing Splitting
| # Quote to prevent splitting
for item in "$words"; do
echo "Item: [$item]" # [one two three]
done
|
๐งช Filename Expansion (Globbing)
Basic Patterns
| # Wildcards
ls *.txt # All .txt files
ls file?.log # file1.log, fileA.log
ls [a-c]*.sh # Files starting with a,b,c
|
Extended Globs (Bash)
| shopt -s extglob
# Match anything except pattern
ls !(temp).txt # All .txt except temp.txt
# Match one of patterns
ls @(file1|file2).txt # file1.txt or file2.txt
# Match zero or one occurrence
ls file?(.bak).txt # file.txt or file.bak.txt
|
Recursive Globbing (Bash 4.3+)
| shopt -s globstar
ls **/*.txt # All .txt files recursively
|
๐ง Expansion Order Examples
Complex Nesting
| # Expansion order demonstration
files=(*.txt)
echo "First file: ${files[0]##*/}" # Basename of first .txt file
# Nested parameter expansion
config="${CONFIG_FILE:-${HOME}/.config/app.conf}"
|
Timing Considerations
| # Expensive expansion - evaluated every time
for i in {1..1000}; do
echo "Date: $(date)" # Slow - 1000 date calls
done
# Better approach - cache result
current_date=$(date)
for i in {1..1000}; do
echo "Date: $current_date" # Fast - 1 call
done
|
๐งพ Portability Notes
| Feature |
POSIX |
Bash |
Zsh |
${var:-default} |
โ
|
โ
|
โ
|
${var:=default} |
โ
|
โ
|
โ
|
${var:?error} |
โ
|
โ
|
โ
|
${var:+alt} |
โ
|
โ
|
โ
|
${var^^} (case) |
โ |
โ
|
โ
|
${!var} (indirect) |
โ |
โ
|
โ
|
$((arithmetic)) |
โ
|
โ
|
โ
|
{1..5} (brace) |
โ |
โ
|
โ
|
| Extended globs |
โ |
โ
|
โ
|
๐งพ Summary
- Master parameter expansions for robust default handling
- Use brace expansion for generating sequences and structures
- Prefer
$() over backticks for command substitution
- Leverage arithmetic expansion for calculations
- Control word splitting with careful quoting and IFS
- Understand expansion order to predict behavior
- Test portability with multiple shell implementations
๐ Continue to: Error Semantics