The $ anchor: end of string, and the trailing newline question
Outside a character class, $ asserts that the current position is the end of the string, or the end of a line under the m flag. JavaScript does not let it match before a final newline.
At a glance
- Syntax
- $
- Width
- zero
- Without the m flag
- matches only at the very end of the string
- Trailing newline
- JavaScript refuses, Python's re and Ruby both allow it
- With the m flag
- also matches directly before every line terminator
- Literal dollar sign
- \$ in the pattern, $$ in a replacement string
In detail
In JavaScript /^\d+$/ rejects the string "42\n". In Python the equivalent pattern accepts it, because $ there also matches at a position just before a single trailing newline, and Ruby behaves the same way. This is the difference that most often breaks a validator moved between languages, and it breaks it in the safe direction only if you are moving towards JavaScript.
The practical consequence is that text read from a file, a textarea or a shell pipeline usually ends in a newline you did not put there. The right fix is to trim the value before validating rather than to loosen the anchor, because a pattern relaxed to tolerate trailing whitespace usually ends up tolerating a good deal more than that.
With m set, $ also matches directly before every line terminator, which is what makes /^\w+$/gm a per line filter. Without m it stays absolute, and that is the correct setting for anything that is answering a question about a whole value rather than about the lines inside it.
The dollar sign in a replacement string is a separate small language with its own rules. There $1 is a group, $& is the whole match, $<name> is a named group, and $$ is the way to produce a literal dollar. Escaping the character in the pattern has no bearing on any of that.
Examples
/^\d+$/.test("42"); // true
/^\d+$/.test("42\n"); // false in JavaScript
// The same pattern in Python's re module matches, because $ there also
// accepts a position immediately before one trailing newline.
"a\nb".match(/a$/); // null
"a\nb".match(/a$/m)[0]; // "a""costs $30".match(/\$\d+/)[0]; // "$30"
// In a replacement string the dollar sign means something else again.
"ab".replace(/(a)(b)/, "$2$1"); // "ba"
"ab".replace(/(a)(b)/, "$$"); // "$"Things that catch people out
- Values arriving from a file or a form field frequently carry a trailing newline. Trim before validating rather than widening the pattern to accept it.
- Porting a validator from Python or Ruby to JavaScript quietly tightens it, because those engines let $ sit before a trailing newline and this one does not. The same input that passed now fails.
- \$ escapes a dollar sign in the pattern and does nothing for a replacement string, where the escape is $$ instead.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →The ^ anchor: start of string, or start of line
Where the caret matches with and without the m flag, why it consumes nothing, and the entirely different job it does inside a character class.
The m flag: multiline anchors
What multiline mode changes about ^ and $, which line terminators JavaScript counts, and why the m flag never makes the dot match a newline.
\A and \z in JavaScript: what to use instead
JavaScript has no \A, \z or \Z. Here is what those anchors silently turn into, and the absolute start and end assertions that survive the m flag.
Anchoring to a line without turning on the m flag
How lookaround assertions give you per line anchors at one position in a pattern while ^ and $ keep their whole string meaning everywhere else.
(...) capturing groups: numbering and what comes back
How capture groups are numbered, what a group that did not participate returns, and why quantifying a group throws away everything but the last pass.
(?:...): grouping without creating a capture
A non capturing group applies quantifiers and alternation without taking a number, which is what keeps $1 pointing at the same thing after an edit.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links