# Regex Cheat Sheet: PCRE, JavaScript, Python and grep -E

Source: https://srvscripts.com/guides/regex-cheat-sheet/
Updated: 2026-10-09
Publisher: srvScripts (https://srvscripts.com/)

**Short answer:** Core regex syntax is shared: `.` `*` `+` `?` `{n,m}`, `[...]` classes, `^` and `$`, groups and `|`. The flavours differ in the extras. PCRE (`grep -P`, PHP) has `\K`, possessive quantifiers and `(?i)`. JavaScript uses flags such as `/gi` and accepts lookbehind of any length. Python uses `(?P<name>...)` and needs fixed-width lookbehind. POSIX ERE (`grep -E`) has no `\d`, lazy quantifiers or lookaround.

The examples in this regex cheat sheet were run on 9 October 2026 with GNU grep 3.11 (grep -E and grep -P with PCRE2 10.42), Python 3.11.15, Node.js 22.22.2, PHP 8.4 (PCRE2) and GNU sed.

## Characters and classes

| Pattern | Matches | Notes |
| --- | --- | --- |
| . | Any character except newline | Add the s flag (DOTALL) to include newlines |
| \d, \w, \s | Digit, word character, whitespace | Not POSIX. GNU grep -E knows \w and \s but treats \d as a plain d |
| \D, \W, \S | The opposites | Same caveat for grep -E |
| [abc], [^abc], [a-z0-9] | One of, none of, ranges | Same in all flavours |
| [[:digit:]], [[:alpha:]], [[:space:]] | POSIX bracket classes | grep -E and PCRE; not JavaScript or Python |
| \., \*, \[ | A literal special character | Escape any of . ^ $ * + ? ( ) [ ] { } | \ |
| \p{L} | Any Unicode letter | PCRE; JavaScript with the u or v flag; not Python re |

Unicode differs too. In Python, `\d` also matches Arabic-Indic and other Unicode digits unless you pass `re.ASCII`. In JavaScript, `\d` and `\w` are ASCII only, even with the `u` flag.

## Anchors and quantifiers

| Pattern | Meaning |
| --- | --- |
| ^, $ | Start and end of the string, or of each line with the multiline flag (m) |
| \b, \B | Word boundary, not a word boundary. GNU grep also has \< and \> |
| \A, \z | Start and end of the whole string in PCRE. Python uses \A and \Z (\z from 3.14); JavaScript has neither |
| *, +, ? | 0 or more, 1 or more, 0 or 1 (greedy: as many as possible) |
| {3}, {2,5}, {2,} | Exactly 3, 2 to 5, 2 or more |
| *?, +?, ?? | Lazy: as few as possible. Not in POSIX ERE |
| *+, ++, (?>...) | Possessive and atomic: never give back characters. PCRE and Python 3.11+, not JavaScript or ERE |

Greedy versus lazy on `<a><b>`:

```
$ echo '' | grep -oP ''

$ echo '' | grep -oE ''

$ echo '' | grep -oE ']+>'

```

In ERE the `?` after `+` just makes the group optional, so the match stays greedy. A negated class such as `[^>]+` works in every flavour.

## Groups and backreferences

| Feature | PCRE | JavaScript | Python | grep -E |
| --- | --- | --- | --- | --- |
| Capture group | (abc) | (abc) | (abc) | (abc) |
| Non-capturing | (?:abc) | (?:abc) | (?:abc) | Not supported |
| Named group | (?...) or (?P...) | (?...) | (?P...) | Not supported |
| Backreference | \1, \k | \1, \k | \1, (?P=ip) | \1 (GNU extension) |
| Alternation | a|b | a|b | a|b | a|b |
| Replacement: group 1 | $1 or \1 (PHP) | $1 | \1 or \g | \1 in sed -E |
| Replacement: named | Not in PHP preg_replace (numbers only) | $ | \g | — |

## Lookahead, lookbehind and \K

| Pattern | Meaning | Support |
| --- | --- | --- |
| foo(?=bar) | foo followed by bar | PCRE, JavaScript, Python |
| foo(?!bar) | foo not followed by bar | PCRE, JavaScript, Python |
| (?Lookbehind length rules differ. JavaScript allows any length. Python needs a fixed width, so even `(?<=x=|yy=)` fails with “look-behind requires fixed-width pattern”. PCRE2 accepts alternatives of different fixed lengths, and from 10.43 also bounded variable lengths such as `(?<=colou?r)`. POSIX ERE has no lookaround at all.

## Flags and modifiers

| Flag | Meaning | PCRE / PHP | JavaScript | Python |
| --- | --- | --- | --- | --- |
| Case-insensitive | a = A | /i, (?i) | /i | re.I, (?i) |
| Multiline | ^ and $ per line | /m, (?m) | /m | re.M |
| Dot matches newline | . includes \n | /s, (?s) | /s | re.S |
| Extended | Ignore spaces, allow # comments | /x, (?x) | Not supported | re.X |
| Global | Find every match | preg_match_all | /g | re.findall |
| Unicode | Full Unicode handling | /u | /u or /v | Default for str |

JavaScript has no `(?i)` prefix: Node.js 22 rejects it as an invalid group. Pass flags after the closing slash or as the second argument to `new RegExp()`. In Python 3.11 and later, a global inline flag such as `(?i)` must be at the very start of the pattern.

## Using regex in Python and JavaScript

| Task | Python | JavaScript |
| --- | --- | --- |
| Test for a match | re.search(r'\d+', s) (anywhere); re.match anchors at the start; re.fullmatch needs the whole string | /\d+/.test(s) |
| All matches | re.findall(r'(\w+)=(\d+)', s) returns tuples when there are groups | [...s.matchAll(/(\w+)=(\d+)/g)] (the g flag is required) |
| Named group | m.group('ip'), m.groupdict() | m.groups.ip |
| Replace | re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', s) | s.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1') |
| Replace all | re.sub replaces all by default | s.replace(/x/g, 'y') or s.replaceAll(/x/g, 'y'); replaceAll throws a TypeError without g |
| Split | re.split(r'\s*,\s*', s) | s.split(/\s*,\s*/) |
| Escape user input | re.escape(s) | No built-in in Node.js 22; escape .*+?^${}()|[]\ yourself |

A JavaScript regex with the `g` or `y` flag remembers `lastIndex` between calls. Reusing `/o/g` with `.test('foo')` returned true, true, then false on our test. Use a regex without `g` for yes/no tests.

Always write Python patterns as raw strings (`r'\d+'`), so that the backslashes reach the regex engine unchanged.

## grep -E versus grep -P on server logs

For server admins: `grep -E` (POSIX ERE with GNU extensions) is fast and available everywhere, but has no `\d`, lazy quantifiers, non-capturing groups or lookaround. `grep -P` uses PCRE2 and adds all of them, plus `\K`. Both work on a sample of SSH log lines:

```
$ grep -oE 'from [0-9]{1,3}(\.[0-9]{1,3}){3}' auth.log
from 203.0.113.10
from 198.51.100.7
$ grep -oP 'from \K[\d.]+' auth.log
203.0.113.10
198.51.100.7
$ grep -oP '(?
