Regex character "$" doesn't mean "end-of-string"

Regular expression users are debating how the `$` anchor should behave: many expect it to mean “end of string,” but in languages like Python, Java, .NET, PHP, Ruby and Perl it can also match just before a final newline, while JavaScript, Go, Rust and RE2 treat it strictly as end-of-string when multiline mode is off. Commenters trace this to historical line-oriented tools and differing regex dialects (POSIX BRE/ERE, PCRE, RE2, custom engines), noting that these subtle differences can cause real bugs and security issues in input validation. The consensus is that there is no universal regex standard, so developers must read the engine-specific docs, write targeted tests, and use anchors like `\A`/`\Z` where available if they truly want start/end-of-string semantics.

Core dispute: What $ means

  • Thread centers on the fact that in many engines $ in non‑multiline mode matches either end-of-string or just before a trailing \n, not strictly “end-of-string.”
  • Some engines (JS, Go, Rust, RE2-style) make $ equivalent to “end of string” when multiline is off.
  • Others (Python, Java, C#, PHP, Perl, PCRE, etc.) treat $ more like “end of line,” with a special case for a single trailing newline.
  • Several commenters find the Python/Perl-style behavior surprising and “whacky,” especially examples like cat$ not matching cat\n\n.

Line vs string semantics

  • Big subthread argues whether $ is conceptually “end of line” or “end of string.”
  • One side insists the historical, line-based view (from editors, ed/sed/grep) justifies $ treating a trailing newline specially.
  • The other side counters that most APIs take arbitrary strings, not lines, so users reasonably expect $ to mean “end of string.”
  • There is debate over POSIX definitions of “line,” “incomplete line,” and how newline as terminator vs separator should affect $.

Regex dialects and (lack of) standardization

  • Many note there is no single regex standard; there are multiple families:
    • POSIX BRE/ERE, with different anchor semantics.
    • Perl/PCRE and descendants (PHP, many languages, PCRE2).
    • RE2-style, used by Go and Rust’s regex crate, which deliberately avoid some Perl quirks.
    • JavaScript’s ECMA regex, with its own gaps and features.
  • C++ <regex>, ECMA-262, POSIX, and a recent RFC (I-Regexp) are mentioned as formal specs, but they coexist rather than unify behavior.

Security and correctness implications

  • $ matching before a trailing newline has led to real bugs, e.g., in Ruby input validation and ExifTool/GitLab vulnerabilities, where attackers smuggle payloads after a newline.
  • Recommendation in several comments: when you truly mean “whole string,” use \A...\Z or \z where available, not ^...$.

Tooling, usage patterns, and ergonomics

  • Many advise always checking the specific engine’s docs and testing regexes explicitly.
  • Some prefer line-oriented tools (grep/sed/awk, editors like Vim) and think in terms of lines; others primarily work with in-memory strings and expect string semantics.
  • Broader frustration appears around regex flavor fragmentation, escaping rules, and inconsistent support for character classes and anchors.