Bash by Example: Extended Globs
Advanced pattern matching with extended globbing (extglob) enabling complex patterns like +(...) for one or more matches, @(...) for exactly one match, !(...) for negation, *(...) for zero or more, and ?(...) for zero or one, requiring shopt -s extglob to enable, and using extended globs for sophisticated file filtering.
Code
#!/bin/bash
# Enable extended globbing
shopt -s extglob
mkdir -p data
touch data/img.jpg data/img.png data/img.gif data/readme.txt data/script.sh
echo "--- Match One of List (@) ---"
# Matches img.jpg OR img.png
echo data/img.@(jpg|png)
echo -e "\n--- Match One or More (+) ---"
# Matches one or more occurrences
# e.g., would match 'ba' 'baa' 'baaa'
# Here just matching specific files
echo data/img.+(jpg|png|gif)
echo -e "\n--- Negation (!) ---"
# Match everything EXCEPT .txt files
echo data/!(*.txt)
echo -e "\n--- Zero or One (?) ---"
# Matches 'color' or 'colour'
# echo col?(o)ur
# Disable extended globbing (optional, good practice)
shopt -u extglob
rm -rf dataExplanation
Standard globs are powerful, but sometimes you need more complex logic, like "match all files except these" or "match files ending in .jpg or .png". This is where Extended Globbing comes in. You must explicitly enable it using shopt -s extglob (shell option set extended glob).
Extended globs introduce syntax similar to Regular Expressions but strictly for filename expansion:
?(pattern-list): Matches zero or one occurrence.
*(pattern-list): Matches zero or more occurrences.
+(pattern-list): Matches one or more occurrences.
@(pattern-list): Matches exactly one of the given patterns.
!(pattern-list): Matches anything except the given patterns.
The !(...) negation pattern is particularly useful for cleaning up directories. For example, rm !(*.cfg) deletes everything that is not a config file. This is much safer and easier than trying to list every file you want to delete individually.
Code Breakdown
shopt -s extglob is required. Without this, Bash will interpret parentheses as syntax errors or subshells rather than pattern matchers.@(jpg|png) acts like an "OR" operator. It matches exactly one of the patterns inside the parentheses.!(*.txt) is the "NOT" operator. It matches img.jpg, script.sh, etc., but ignores readme.txt.
