Zsh: glob case-insensitively with the (#i) glob flag and the no_case_glob option

Just in case…

By default, Zsh globs are case-sensitive, so *.jpg matches beach.jpg but not CAT.JPG or dog.Jpg:

$ ls photos/*.jpg
photos/beach.jpg

This is true even on macOS, where the filesystem is case-insensitive by default.

Zsh shares this behaviour with Bash, but provides per-glob and global controls to make your globs case-insensitive and match all the files you probably meant.

Per-glob with (#i)

To make a single glob case-insensitive, use the (#i) glob flag, which makes the rest of the pattern case-insensitive:

$ ls photos/*.(#i)jpg
photos/CAT.JPG  photos/beach.jpg  photos/dog.Jpg

Glob flags require the extended_glob option, which you can enable in your ~/.zshrc:

setopt extended_glob

Without it, Zsh treats (#i) as part of the pattern, leading to a confusing “no matches found” error.

Flag placement

(#i) applies from where it appears until the end of the pattern, including across directory separators. So you can put it at the start to make the whole pattern case-insensitive. For example, to find a README, however it’s capitalized:

$ ls (#i)readme*
README.md  ReadMe.rst  readme.txt

Or, to match a directory name of unknown case:

$ ls (#i)photos/*.jpg
Photos/CAT.JPG  Photos/beach.jpg  Photos/dog.Jpg

By default with no_case_glob

Case sensitivity is controlled by a few options, which you can check with set -o:

$ set -o | grep case
nocaseglob            off
nocasematch           off
casepaths             off

set -o lists options that are on by default in their “no” form, so nocaseglob off means that the case_glob option is on, making globs case-sensitive. (case_match is the equivalent for regular expressions matched with =~.)

If you want all globs to be case-insensitive, you can turn on no_case_glob in your ~/.zshrc:

setopt no_case_glob

Then *.jpg matches CAT.JPG and friends, without any flag.

This applies to every directory in a path that contains a glob, so photos/*.jpg also matches files in Photos/. If you’d rather that literal directory names stay case-sensitive, also turn on the case_paths option:

setopt case_paths

Fin

May your globs match, whatever the case,

—Adam


Check out my new book Boost Your GitHub DX.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: