Linux Basic — POSIX Character Classes

2 minute read

POSIX Character Classes

\d, \w, \s are Perl-style. They don’t work in BRE, ERE, awk, or Bash =~. The portable equivalents are POSIX character classes: [[:digit:]], [[:alpha:]], etc.

They’re more verbose but they work everywhere a POSIX regex engine runs — and they’re locale-aware, so [[:alpha:]] matches accented letters in en_US.UTF-8 while [A-Za-z] does not.


The Full List

| Class | Equivalent (ASCII) | Matches | |——-|———————|———| | [[:alpha:]] | [A-Za-z] | letters | | [[:digit:]] | [0-9] | digits | | [[:alnum:]] | [A-Za-z0-9] | letters + digits | | [[:upper:]] | [A-Z] | upper-case | | [[:lower:]] | [a-z] | lower-case | | [[:space:]] | [ \t\n\r\f\v] | whitespace | | [[:blank:]] | [ \t] | space or tab only | | [[:punct:]] | [!-/:-@\[-{-~] | punctuation | | [[:xdigit:]] | [0-9A-Fa-f] | hex digit | | [[:cntrl:]] | control chars | non-printable | | [[:print:]] | printable + space | opposite of cntrl | | [[:graph:]]` | printable, not space | visible chars |


Syntax — Note the Double Brackets

A character class lives inside a bracket expression. So a regex character class needs two sets of brackets:

# match one digit
grep -E '[[:digit:]]'      # correct
grep -E '[:digit:]'        # WRONG — matches one of : d i g t

You can combine multiple classes and literals inside the outer brackets:

# identifier: starts with letter/underscore, then letters/digits/underscore
grep -E '^[[:alpha:]_][[:alnum:]_]*$'

Quantify Them

# 4+ uppercase letters
grep -E '[[:upper:]]{4,}'

# whitespace-tolerant key=value
grep -E '^[[:alnum:]_]+[[:space:]]*=[[:space:]]*.+$'

# trim leading whitespace with sed
sed -E 's/^[[:space:]]+//'

Negate a Class

Put ^ after the outer [:

# anything that's NOT a digit
grep -E '[^[:digit:]]'

# strip non-alphanumerics
sed -E 's/[^[:alnum:]]+/_/g'

Why Prefer POSIX Classes Over [A-Z0-9]?

  1. Portable. Works in grep, sed, awk, Bash =~, plus other Unix tools — without escaping or -E quirks.
  2. Locale-aware. In a UTF-8 locale [[:alpha:]] includes é, ñ, ü. [A-Za-z] does not.
  3. Self-documenting. [[:xdigit:]] reads as “hex digit”; [0-9A-Fa-f] does not.

When you need ASCII-only matching regardless of locale, set the locale explicitly:

LC_ALL=C grep -E '[[:alpha:]]+' input

LC_ALL=C also makes grep/sort dramatically faster on ASCII data.


Bash =~ with POSIX Classes

Same syntax, no surprises:

if [[ "$word" =~ ^[[:alpha:]][[:alnum:]_]*$ ]]; then
    echo "valid identifier"
fi

Cheat-Sheet

You want… Use
Digit [[:digit:]]
Word char (letter+digit+_) [[:alnum:]_] (POSIX has no \w)
Whitespace [[:space:]]
Non-whitespace [^[:space:]]
Hex [[:xdigit:]]
Printable, no space [[:graph:]]

Next → sed Regex in Scripts

Updated: