The original legacy PCRE allows unescaped hyphens in some contexts and treats them as literal characters instead of range separators.
PCRE2 is much stricter about that and requires most hyphens to be escaped.
utop # #require "pcre";;
utop # #require "pcre2";;
utop # let r = Pcre.regexp {| [[:alnum:]-_!@*#]{1,100} |};;
val r : Pcre.regexp = <abstr>
utop # let r' = Pcre2.regexp {| [[:alnum:]-_!@*#]{1,100} |};;
Exception:
Pcre2.Error(Pcre2.BadPattern("invalid range in character class\000\000\000\000\000\000\000\000PrD\217o\127\000\000\000\000\000\000\000\000\000\000\248c\166\201o\127\000\000\000\000\000\000\000\000\000\000x\245\170\201o\127\000\000\000\000\000\000\000\000\000\000H\018\170\201o\127\000\000\001\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000", pos=11))
utop # let r' = Pcre2.regexp {| [[:alnum:]\-_!@*#]{1,100} |};;
val r' : Pcre2.regexp = <abstr>That breaks some validation regexes we have in our command definitions, as T7464 shows.
Escaped hyphens work in legacy PCRE, so we can solve the issue by always escaping them.