Working on the command line often involves manipulating groups of
files. While basic wildcards like *
are useful for including
files, what happens when you need to perform an action on almost
everything in a directory, excluding just a few specific types?
Manually selecting files or piping ls
through grep -v
can be
cumbersome.
Fortunately, Zsh (the Z shell) offers powerful “globbing” (filename
generation) features that make excluding files elegant and efficient.
Today, we’ll focus on two incredibly useful operators available
under Zsh’s EXTENDED_GLOB
option: the exclusion operator (~
)
and the negation operator (^
).
Prerequisite: Enabling EXTENDED_GLOB
Both ~
and ^
require the EXTENDED_GLOB
option to be enabled
in Zsh. You can enable it for your current session like this:
setopt EXTENDED_GLOB
To make this permanent, add that line to your Zsh configuration
file, typically ~/.zshrc
:
# ~/.zshrc
setopt EXTENDED_GLOB
Now, let’s dive into the operators.
Method 1: The Exclusion Operator (~
)
Think of the ~
operator as meaning “match the pattern on the left,
but not if it also matches the pattern on the right.” It’s an
intuitive way to subtract specific patterns from a broader match.
Syntax: pattern_to_match ~ pattern_to_exclude
Common Use Case: Match everything (*
), but exclude specific extensions.
Example: Excluding .bak
files
Imagine a directory with source files, compiled files, and backup
(.bak
) files. To list everything except the backups:
# Ensure EXTENDED_GLOB is set
setopt EXTENDED_GLOB
# List all files except those ending in .bak
ls *~*.bak
*
: Matches all files initially.~
: Excludes anything matching the following pattern.*.bak
: Matches files ending with.bak
.
Example: Excluding Multiple Extensions (.bak
and .zip
)
Need to exclude more than one type? Use parentheses ()
to group
the patterns-to-exclude, separated by the pipe |
(OR) symbol.
# List all files except those ending in .bak OR .zip
ls *~(*.bak|*.zip)
*
: Matches all files.~
: Excludes…(*.bak|*.zip)
: …anything matching*.bak
OR*.zip
.
Method 2: The Negation Operator (^
)
The ^
operator is more direct: it matches anything that does
not conform to the pattern following it.
Syntax: ^pattern_to_avoid
Common Use Case: Match any file that doesn’t fit a specific pattern.
Example: Excluding .bak
files
Using ^
, we achieve the same result as with ~
for single exclusions:
# Ensure EXTENDED_GLOB is set
setopt EXTENDED_GLOB
# List all files that DO NOT end in .bak
ls ^*.bak
^
: Match files that do not match the following pattern.*.bak
: Files ending with.bak
.
Example: Excluding Multiple Extensions (.bak
and .zip
)
Similarly, use ()
and |
to negate multiple patterns at once.
# List all files that DO NOT end in .bak OR .zip
ls ^(*.bak|*.zip)
^
: Match files that do not match…(*.bak|*.zip)
: …the pattern*.bak
OR*.zip
.
Choosing Between ~
and ^
For the common task of excluding specific extensions from a full
directory listing (*
), both *~pattern
and ^pattern
often yield
the same results.
*~pattern
reads slightly more like “everything except pattern”.^pattern
reads more like “not pattern”.
The choice often comes down to personal preference and which feels more logical for the specific task at hand.
What About Bash?
This is a crucial point: The ~
and ^
globbing operators, as
described here, are specific to Zsh. They are not available in
Bash by default.
Bash does have its own extended globbing capabilities, enabled
via shopt -s extglob
. The closest equivalent in Bash for exclusion
is the !(pattern-list)
syntax.
Bash Example (Equivalent to ls ^(*.bak|*.zip)
):
# Enable extended globbing in Bash
shopt -s extglob
# List files not ending in .bak or .zip
ls !(*.bak|*.zip)
# Remember to disable if needed: shopt -u extglob
While functional, many find Zsh’s ~
and ^
syntax slightly cleaner
and more readable for these exclusion tasks.
Practical Use Cases
Mastering these operators streamlines many command-line tasks:
- Cleaning Directories:
rm *~(*.log|*.tmp)
- Remove everything except log and temp files. - Archiving Projects:
tar czf project.tar.gz *~(*.o|*.bak|*.zip)
- Archive source files, excluding object files, backups, and other archives. - File Processing:
process_script ^(*.txt|*.md)
- Run a script on all files that aren’t text or markdown. - Scripting: Make shell scripts more robust and less reliant on complex
find
orgrep
pipelines for simple exclusions.
Conclusion
Zsh’s EXTENDED_GLOB
feature, particularly the ~
(exclusion) and
^
(negation) operators, provide a powerful and elegant way to
select files while excluding specific patterns. By understanding
and utilizing these operators, you can make your command-line work
faster, your commands clearer, and your scripts more efficient.
While Bash offers alternatives via extglob
, Zsh’s syntax often
excels for its readability in exclusion scenarios.