Where to start First steps track · 15 stages
Linux Labs

Glob patterns and brace expansion

4 min bash

Shell substitutions — * ? [] {} — and how they differ from regular expressions.

Cheat sheet

9 commands Printable version
  • *.log ? [0-9] [!a] glob: string / char / range / negated
  • mkdir -p p/{src,tests,docs} brace expansion
  • cp file{,.bak} a quick backup
  • echo {1..5} a sequence
  • echo {01..10..2} a sequence with a step
  • shopt -s globstar enable recursive **
  • ls **/*.log a recursive pattern search
  • shopt -s extglob enable extended patterns
  • rm !(*.keep) remove everything except *.keep
  • glob works on filenames, regex works on content (grep/sed)

Overview

The shell expands filename patterns (globbing) itself, before running the command. This is not regex — the syntax is different and simpler.

bash
ls *.log                          # every file ending in .log
rm tmp_?                          # tmp_1, tmp_a (one character)
ls report-[0-9].txt               # report-0.txt … report-9.txt

Options

bash
shopt -s globstar     # ** recursively: ls **/*.log
shopt -s nullglob     # an unmatched glob → empty (not the pattern itself)
shopt -s dotglob      # * includes hidden files (.bashrc etc.)
shopt -s nocaseglob   # case-insensitive: *.JPG matches .jpg too
shopt -s extglob      # extended patterns: !(*.bak) @(a|b) +(x) *(y) ?(z)
shopt -s failglob     # an unmatched glob → an error (strict scripts)

extglob examples: rm !(*.keep) (everything except .keep), ls @(*.jpg|*.png).

Recipes

2

Patterns

Pattern Meaning
* any string (including empty), except /
? exactly one character
[abc] [!abc] one of / anything but
[0-9] [a-z] a range
** recursively through subdirectories (with shopt -s globstar)

Brace expansion {} is not a glob (it always expands, even with no matching files):

bash
mkdir -p project/{src,tests,docs}     # three directories
cp file.conf{,.bak}                   # cp file.conf file.conf.bak (a quick backup!)
mv app.{js,ts}                        # does NOT work as a rename — it's two arguments
echo {1..5} ; echo {01..10..2}        # 1 2 3 4 5 ; 01 03 05 07 09 (with a step)
echo img-{a,b,c}.png                  # img-a.png img-b.png img-c.png

Glob vs. regex

  • glob is for filenames in the shell; * means "anything".
  • regex is for content in grep/sed; there * means "0 or more of the previous thing", and "anything" is .*.

Don't confuse them: ls *.txt (glob) is not grep '*.txt' (regex — you need grep '\.txt').

Troubleshooting

4

The pattern found no files — the command got the literal pattern

By default bash leaves an unmatched glob as-is (ls *.xyz → "no such file: *.xyz"). Turn on shopt -s nullglob (it vanishes) or failglob (an error).

Hidden files didn't get caught by *

* doesn't include files starting with .. Use .* separately, or shopt -s dotglob (careful: .* also matches . and ..).

Spaces/special characters in names break things

Quote your variables; for bulk processing, use find -print0 | xargs -0 (see xargs).

{a,b} didn't expand

There must be no spaces inside: {a, b} won't work. And it's not a glob — it expands before files are even checked.

See also

  • files-basics — Navigation and files — ls, cd, cp, mv, rm, cat, less, head, tail, terminal copy-paste.
  • find — Finding files by name, size, time, permissions; acting on what's found.
  • regex — Patterns for grep/sed/awk — characters, classes, anchors, groups; basic vs. extended.
Loading…