Emergency server help: get in touch

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

Regular expression syntax with the differences between PCRE, JavaScript, Python and grep -E, tested on real log lines.

Published 6 min read

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

PatternMatchesNotes
.Any character except newlineAdd the s flag (DOTALL) to include newlines
\d, \w, \sDigit, word character, whitespaceNot POSIX. GNU grep -E knows \w and \s but treats \d as a plain d
\D, \W, \SThe oppositesSame caveat for grep -E
[abc], [^abc], [a-z0-9]One of, none of, rangesSame in all flavours
[[:digit:]], [[:alpha:]], [[:space:]]POSIX bracket classesgrep -E and PCRE; not JavaScript or Python
\., \*, \[A literal special characterEscape any of . ^ $ * + ? ( ) [ ] { } | \
\p{L}Any Unicode letterPCRE; 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

PatternMeaning
^, $Start and end of the string, or of each line with the multiline flag (m)
\b, \BWord boundary, not a word boundary. GNU grep also has \< and \>
\A, \zStart 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 '<a><b>' | grep -oP '<.+?>'
<a>
<b>
$ echo '<a><b>' | grep -oE '<.+?>'
<a><b>
$ echo '<a><b>' | grep -oE '<[^>]+>'
<a>
<b>

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

FeaturePCREJavaScriptPythongrep -E
Capture group(abc)(abc)(abc)(abc)
Non-capturing(?:abc)(?:abc)(?:abc)Not supported
Named group(?<ip>...) or (?P<ip>...)(?<ip>...)(?P<ip>...)Not supported
Backreference\1, \k<ip>\1, \k<ip>\1, (?P=ip)\1 (GNU extension)
Alternationa|ba|ba|ba|b
Replacement: group 1$1 or \1 (PHP)$1\1 or \g<1>\1 in sed -E
Replacement: namedNot in PHP preg_replace (numbers only)$<ip>\g<ip>—

Lookahead, lookbehind and \K

PatternMeaningSupport
foo(?=bar)foo followed by barPCRE, JavaScript, Python
foo(?!bar)foo not followed by barPCRE, JavaScript, Python
(?<=user )\w+Word preceded by user PCRE, JavaScript, Python
(?<!\$)\d+Digits not preceded by $PCRE, JavaScript, Python
from \K[\d.]+Match from , then drop it from the resultPCRE only (grep -P, PHP)

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

FlagMeaningPCRE / PHPJavaScriptPython
Case-insensitivea = A/i, (?i)/ire.I, (?i)
Multiline^ and $ per line/m, (?m)/mre.M
Dot matches newline. includes \n/s, (?s)/sre.S
ExtendedIgnore spaces, allow # comments/x, (?x)Not supportedre.X
GlobalFind every matchpreg_match_all/gre.findall
UnicodeFull Unicode handling/u/u or /vDefault 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

TaskPythonJavaScript
Test for a matchre.search(r'\d+', s) (anywhere); re.match anchors at the start; re.fullmatch needs the whole string/\d+/.test(s)
All matchesre.findall(r'(\w+)=(\d+)', s) returns tuples when there are groups[...s.matchAll(/(\w+)=(\d+)/g)] (the g flag is required)
Named groupm.group('ip'), m.groupdict()m.groups.ip
Replacere.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 allre.sub replaces all by defaults.replace(/x/g, 'y') or s.replaceAll(/x/g, 'y'); replaceAll throws a TypeError without g
Splitre.split(r'\s*,\s*', s)s.split(/\s*,\s*/)
Escape user inputre.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 '(?<=invalid user )\w+' auth.log
admin
TaskCommand
Count matching linesgrep -cE 'Failed|Accepted' auth.log
Whole line must matchgrep -xE '[0-9]+' file
Case-insensitivegrep -iE 'error|fatal' app.log
Show only the matchgrep -oE '[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+' log | sort -u
Reorder with sedsed -E 's/([0-9]+)-([0-9]+)-([0-9]+)/\3\/\2\/\1/' turns 2026-10-09 into 09/10/2026

Do not use \d with grep -E: on our test, GNU grep 3.11 matched the letter d in sshd instead of a digit and printed no warning. Use [0-9] or [[:digit:]], or switch to grep -P.

Official documentation (checked 9 October 2026): PCRE2 pattern syntax (pcre2pattern) · MDN: Regular expressions (JavaScript) · Python: re — Regular expression operations · POSIX: Regular Expressions · GNU Grep manual · PHP: PCRE regex syntax

Regex Cheat Sheet at a glance

Regex Cheat Sheet – short answer
In short: Regular expression syntax with the differences between PCRE, JavaScript, Python and grep -E, tested on real log lines.
Regex Cheat Sheet – what this guide covers
Regex Cheat Sheet: what this guide covers.

Related: Regex Tester for JavaScript · Terminal Shortcuts Cheat Sheet: Bash, Zsh and tmux · Python Cheat Sheet: Syntax, Data Types and File Handling.

Reading a lot of our guides? Go ad-free with srvScripts Pro for $6 a month. It also pays for new guides. Cancel any time.

Share this guide

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.