OS X sed -E doesn't accept extended regular expressions - regex

I've been trying various ways to do some basic things with sed on OS X. Here are the results of some simple tests.
echo "foo bar 2011-03-17 17:31:47 foo bar" | sed 's/foo/FOUND/g'
returns (as expected)
FOUND bar 2011-03-17 17:31:47 FOUND bar
but
echo "foo bar 2011-03-17 17:31:47 foo bar" | sed -E 's/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/FOUND/g'
returns
foo bar 2011-03-17 17:31:47 foo bar
and (even more irritatingly)
echo "food bar 2011-03-17 17:31:47 food bar" | sed -E 's/\d/FOUND/g'
returns
fooFOUND bar 2011-03-17 17:31:47 fooFOUND bar
Now, the man sed pages say that
The following options are available:
-E Interpret regular expressions as extended (modern) regular
expressions rather than basic regular expressions (BRE's). The
re_format(7) manual page fully describes both formats.
and man re_format says
\d Matches a digit character. This is equivalent to
`[[:digit:]]'.
And indeed:
echo "foo bar 2011-03-17 17:31:47 foo bar" | sed -E 's/[[:digit:]]{4}/FOUND/g'
gives me
foo bar FOUND-03-17 17:31:47 foo bar
...but this is annoying. Either because I'm being dense, or because the man pages are lying to me (to be honest, I'd prefer the former).
A quick literature review here on SO suggests that I am not alone in this, and that many recommend installing GNU coreutils (or indeed use something else - say perl -pe) -- however, I'd like to be certain:
Do EREs work with sed as it is bundled with OS X -- as implied by the man pages -- or not?
(I'm on 10.8 and 10.6.8)

On macOS, \d is part of a regex feature set called enhanced features - note the distinction in name: enhanced, which is NOT the same as extended.
Instead, enhanced features are a separate dimension from basic vs. extended, which can be activated for both basic and extended regexes. In other words: you can have enhanced basic regexes as well as enhanced extended regexes.
However, it appears that whether enhanced features are available in a given utility is precompiled into it; in other words: a given utility either supports enhanced features or it doesn't - no option can change that.
(Options only allow you to choose between basic and extended, such as -E for sed and grep.)
For a description of all enhanced features, see section ENHANCED FEATURES in man re_format.
It should also be noted that if POSIX compatibility is important, enhanced features should be avoided with sed.
There are POSIX utilities, such as awk, that do support EREs (extended regular expressions), but (a), the POSIX spec explicitly has to state so, and (b) the syntax is limited to POSIX EREs, which are less powerful than the EREs offered by specific platforms.
In practice:
Sadly, the man pages for the various utilities do NOT state whether a given utility supports enhanced regex features, so it comes down to trial and error.
As of macOS 10.15:
macOS sed does NOT support enhanced features, which explains the OP's experience.
E.g., sed -E 's/\d//g' <<<'a10' has no effect, because \d isn't recognized as representing a digit (only [[:digit:]] is).
I have found only one utility that supports enhanced features: grep:
grep -o '\d\+' <<<'a10' # -> '10' - enhanced basic regex
grep -E -o '\d+' <<<'a10' # -> '10' - enhanced extended regex
If you know of others that do, please let us know.

ERE (Extended Regular Expressions) are described in POSIX under (surprise) Regular Expressions or on Mac OS X under man re_format. EREs do not use the PCRE style \d notation to mean digit.
You will need to use either [0-9] or [[:digit:]] to mean digit.
$ echo "foo bar 2011-03-17 17:31:47 foo bar" |
> sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/FOUND/g'
foo bar FOUND foo bar
$ echo "foo bar 2011-03-17 17:31:47 foo bar" |
> sed -E 's/[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}/FOUND/g'
foo bar FOUND foo bar
$
What about \d?
On my Mac OS X (10.7.4), the man re_format does not say anything about \d matching digits.
RE_FORMAT(7) BSD Miscellaneous Information Manual RE_FORMAT(7)
NAME
re_format -- POSIX 1003.2 regular expressions
DESCRIPTION
Regular expressions (``REs''), as defined in IEEE Std 1003.2
(``POSIX.2''), come in two forms: modern REs (roughly those of egrep(1);
1003.2 calls these ``extended'' REs) and obsolete REs (roughly those of
ed(1); 1003.2 ``basic'' REs). Obsolete REs mostly exist for backward
compatibility in some old programs; they will be discussed at the end.
IEEE Std 1003.2 (``POSIX.2'') leaves some aspects of RE syntax and seman-
tics open; `=' marks decisions on these aspects that may not be fully
portable to other IEEE Std 1003.2 (``POSIX.2'') implementations.
A (modern) RE is one= or more non-empty= branches, separated by `|'. It
matches anything that matches one of the branches.
A branch is one= or more pieces, concatenated. It matches a match for
the first, followed by a match for the second, etc.
A piece is an atom possibly followed by a single= `*', `+', `?', or
bound. An atom followed by `*' matches a sequence of 0 or more matches
of the atom. An atom followed by `+' matches a sequence of 1 or more
matches of the atom. An atom followed by `?' matches a sequence of 0 or
1 matches of the atom.
A bound is `{' followed by an unsigned decimal integer, possibly followed
by `,' possibly followed by another unsigned decimal integer, always fol-
lowed by `}'. The integers must lie between 0 and RE_DUP_MAX (255=)
inclusive, and if there are two of them, the first may not exceed the
second. An atom followed by a bound containing one integer i and no
comma matches a sequence of exactly i matches of the atom. An atom fol-
lowed by a bound containing one integer i and a comma matches a sequence
of i or more matches of the atom. An atom followed by a bound containing
two integers i and j matches a sequence of i through j (inclusive)
matches of the atom.
An atom is a regular expression enclosed in `()' (matching a match for
the regular expression), an empty set of `()' (matching the null
string)=, a bracket expression (see below), `.' (matching any single
character), `^' (matching the null string at the beginning of a line),
`$' (matching the null string at the end of a line), a `\' followed by
one of the characters `^.[$()|*+?{\' (matching that character taken as an
ordinary character), a `\' followed by any other character= (matching
that character taken as an ordinary character, as if the `\' had not been
present=), or a single character with no other significance (matching
that character). A `{' followed by a character other than a digit is an
ordinary character, not the beginning of a bound=. It is illegal to end
an RE with `\'.
A bracket expression is a list of characters enclosed in `[]'. It nor-
mally matches any single character from the list (but see below). If the
list begins with `^', it matches any single character (but see below) not
from the rest of the list. If two characters in the list are separated
by `-', this is shorthand for the full range of characters between those
two (inclusive) in the collating sequence, e.g. `[0-9]' in ASCII matches
any decimal digit. It is illegal= for two ranges to share an endpoint,
e.g. `a-c-e'. Ranges are very collating-sequence-dependent, and portable
programs should avoid relying on them.
To include a literal `]' in the list, make it the first character (fol-
lowing a possible `^'). To include a literal `-', make it the first or
last character, or the second endpoint of a range. To use a literal `-'
as the first endpoint of a range, enclose it in `[.' and `.]' to make it
a collating element (see below). With the exception of these and some
combinations using `[' (see next paragraphs), all other special charac-
ters, including `\', lose their special significance within a bracket
expression.
Within a bracket expression, a collating element (a character, a multi-
character sequence that collates as if it were a single character, or a
collating-sequence name for either) enclosed in `[.' and `.]' stands for
the sequence of characters of that collating element. The sequence is a
single element of the bracket expression's list. A bracket expression
containing a multi-character collating element can thus match more than
one character, e.g. if the collating sequence includes a `ch' collating
element, then the RE `[[.ch.]]*c' matches the first five characters of
`chchcc'.
Within a bracket expression, a collating element enclosed in `[=' and
`=]' is an equivalence class, standing for the sequences of characters of
all collating elements equivalent to that one, including itself. (If
there are no other equivalent collating elements, the treatment is as if
the enclosing delimiters were `[.' and `.]'.) For example, if `x' and
`y' are the members of an equivalence class, then `[[=x=]]', `[[=y=]]',
and `[xy]' are all synonymous. An equivalence class may not= be an end-
point of a range.
Within a bracket expression, the name of a character class enclosed in
`[:' and `:]' stands for the list of all characters belonging to that
class. Standard character class names are:
alnum digit punct
alpha graph space
blank lower upper
cntrl print xdigit
These stand for the character classes defined in ctype(3). A locale may
provide others. A character class may not be used as an endpoint of a
range.
A bracketed expression like `[[:class:]]' can be used to match a single
character that belongs to a character class. The reverse, matching any
character that does not belong to a specific class, the negation operator
of bracket expressions may be used: `[^[:class:]]'.
There are two special cases= of bracket expressions: the bracket expres-
sions `[[:<:]]' and `[[:>:]]' match the null string at the beginning and
end of a word respectively. A word is defined as a sequence of word
characters which is neither preceded nor followed by word characters. A
word character is an alnum character (as defined by ctype(3)) or an
underscore. This is an extension, compatible with but not specified by
IEEE Std 1003.2 (``POSIX.2''), and should be used with caution in soft-
ware intended to be portable to other systems.
In the event that an RE could match more than one substring of a given
string, the RE matches the one starting earliest in the string. If the
RE could match more than one substring starting at that point, it matches
the longest. Subexpressions also match the longest possible substrings,
subject to the constraint that the whole match be as long as possible,
with subexpressions starting earlier in the RE taking priority over ones
starting later. Note that higher-level subexpressions thus take priority
over their lower-level component subexpressions.
Match lengths are measured in characters, not collating elements. A null
string is considered longer than no match at all. For example, `bb*'
matches the three middle characters of `abbbc',
`(wee|week)(knights|nights)' matches all ten characters of `weeknights',
when `(.*).*' is matched against `abc' the parenthesized subexpression
matches all three characters, and when `(a*)*' is matched against `bc'
both the whole RE and the parenthesized subexpression match the null
string.
If case-independent matching is specified, the effect is much as if all
case distinctions had vanished from the alphabet. When an alphabetic
that exists in multiple cases appears as an ordinary character outside a
bracket expression, it is effectively transformed into a bracket expres-
sion containing both cases, e.g. `x' becomes `[xX]'. When it appears
inside a bracket expression, all case counterparts of it are added to the
bracket expression, so that (e.g.) `[x]' becomes `[xX]' and `[^x]'
becomes `[^xX]'.
No particular limit is imposed on the length of REs=. Programs intended
to be portable should not employ REs longer than 256 bytes, as an imple-
mentation can refuse to accept such REs and remain POSIX-compliant.
Obsolete (``basic'') regular expressions differ in several respects. `|'
is an ordinary character and there is no equivalent for its functional-
ity. `+' and `?' are ordinary characters, and their functionality can be
expressed using bounds (`{1,}' or `{0,1}' respectively). Also note that
`x+' in modern REs is equivalent to `xx*'. The delimiters for bounds are
`\{' and `\}', with `{' and `}' by themselves ordinary characters. The
parentheses for nested subexpressions are `\(' and `\)', with `(' and `)'
by themselves ordinary characters. `^' is an ordinary character except
at the beginning of the RE or= the beginning of a parenthesized subex-
pression, `$' is an ordinary character except at the end of the RE or=
the end of a parenthesized subexpression, and `*' is an ordinary charac-
ter if it appears at the beginning of the RE or the beginning of a paren-
thesized subexpression (after a possible leading `^'). Finally, there is
one new type of atom, a back reference: `\' followed by a non-zero deci-
mal digit d matches the same sequence of characters matched by the dth
parenthesized subexpression (numbering subexpressions by the positions of
their opening parentheses, left to right), so that (e.g.) `\([bc]\)\1'
matches `bb' or `cc' but not `bc'.
SEE ALSO
regex(3)
Regular Expression Notation, IEEE Std, 1003.2, section 2.8.
BUGS
Having two kinds of REs is a botch.
The current IEEE Std 1003.2 (``POSIX.2'') spec says that `)' is an ordi-
nary character in the absence of an unmatched `('; this was an uninten-
tional result of a wording error, and change is likely. Avoid relying on
it.
Back references are a dreadful botch, posing major problems for efficient
implementations. They are also somewhat vaguely defined (does
`a\(\(b\)*\2\)*d' match `abbbd'?). Avoid using them.
IEEE Std 1003.2 (``POSIX.2'') specification of case-independent matching
is vague. The ``one case implies all cases'' definition given above is
current consensus among implementors as to the right interpretation.
The syntax for word boundaries is incredibly ugly.
BSD March 20, 1994 BSD

Related

Why is POSIX collating-related bracketed symbol higher-precedence than backslash?

POSIX, aka "The Open Group Base Specifications Issue 7, 2018 edition", has this to say about regular expression operator precedence:
9.4.8 ERE Precedence
The order of precedence shall be as shown in the following table:
ERE Precedence (from high to low)
Collation-related bracket symbols
[==] [::] [..]
Escaped characters
\ special-character
Bracket expression
[]
Grouping
()
Single-character-ERE duplication
* + ? {m,n}
Concatenation
ab
Anchoring
^ $
Alternation
|
I am curious as to the reason for the first two levels being in that order. Being a unix user from way back, I am accustomed to being able to "throw a backslash in front of it" to escape virtually anything. But it appears that with Collation-Related-Bracket-Symbols (CRBS), I can't do that. If I want to match a literal [.ch.] I can't just type \[.ch.] and rely on "dot matches dot" to handle things for me. I now have to match something like [[].ch.] (or possibly worse?).
I'm trying, and failing, to imagine what the scenario was when whoever-thought-this-up decided this should be the order. Is there a concrete scenario where having CRBS ranked higher than backslash makes sense, or was this a case of "we don't understand CRBS yet so let's make it higher priority" or ... what, exactly?
At least for Gnu grep, it looks like lib/dfa.c treats the CRBS as one lexical token, as per the function parse_bracket_exp().
For the example given, escaping the special characters (square brackets and dots) seems to give the results you are looking for. You can also match literal dots with [.] which might be easier to see in a regular expression.
$ (echo c;echo '[.ch.]';echo .ch.;echo xchx)|grep '\[\.ch\.\]'
[.ch.]

^ and $ expressed in fundamental operations in regular expressions

I've read a book where it states that all fundamental operations in regular expressions are concatatenation, or(|), closure(*) and parenthesis to override default precedence. Every other operation is just a shortcut for one or more fundamental operations.
For example, (AB)+ shortcut is expanded to (AB)(AB)* and (AB)? to (ε | AB) where ε is empty string. First of all, I looked up ASCII table and I am not sure which charcode is designated to empty string. Is it ASCII 0?
I'd like to figure out how to express the shortcuts ^ and $ as in ^AB or AB$ expression in the fundamental operations, but I am not sure how to do this. Can you help me out how this is expressed in fundamentals?
Regular expressions, the way they are defined in mathematics, are actually string generators, not search patterns. They are used as a convenient notation for a certain class of sets of strings. (Those sets can contain an infinite number of strings, so enumerating all elements is not practical.)
In a programming context, regexes are usually used as flexible search patterns. In mathematical terms we're saying, "find a substring of the target string S that is an element of the set generated by regex R". This substring search is not part of the regex proper; it's like there's a loop around the actual regex engine that tries to match every possible substring against the regex (and stops when it finds a match).
In fundamental regex terms, it's like there's an implicit .* added before and after your pattern. When you look at it this way, ^ and $ simply prevent .* from being added at the beginning/end of the regex.
As an aside, regexes (as commonly used in programming) are not actually "regular" in the mathematical sense; i.e. there are many constructs that cannot be translated to the fundamental operations listed above. These include backreferences (\1, \2, ...), word boundaries (\b, \<, \>), look-ahead/look-behind assertions ((?= ), (?! ), (?<= ), (?<! )), and others.
As for ε: It has no character code because the empty string is a string, not a character. Specifically, a string is a sequence of characters, and the empty string contains no characters.
^AB can be expressed as (εAB) ie an empty string followed by AB and AB$ can be expressed as (ABε) that's AB followed by an empty string.
The empty string is actually defined as '', that's a string of 0 length, so has no value in the ASCII table. However the C programming language terminates all strings with the ASCII NULL character, although this is not counted in the length of the string it still must be accounted for when allocating memory.
EDIT
As #melpomene pointed out in their comment εAB is equivalent to AB which makes the above invalid. Having talked to a work college I'm no longer sure how to do this or even if it's possible. Hopefully someone can come up with an answer.

Is plus (+) part of basic regular expressions?

Recently I was told, that + (one or more occurrence of the previous pattern/character) is not part of basic regex. Not even when written as \+.
It was on a question about maximum compatibility.
I was under the impression that ...
echo "Hello World, I am an example-text" | sed 's#[^a-z0-9]\+#.#ig'
... always results in:
Hello.World.I.am.an.example.text
But then I was told that "it replaces every character not lowercase or a digit followed by + " and that it is the same as [^a-z0-9][+].
So my real question: is there any regex definition or implementation that does not treat either x+ or x\+ the same as xx*.
POSIX "basic" regular expressions do not support + (nor ?!). Most implementations of sed add support for \+ but it's not a POSIX standard feature. If your goal is maximum portability you should avoid using it. Notice that you have to use \+ rather than the more common +.
echo "Hello World, I am an example-text" | sed 's#[^a-z0-9]\+#.#ig'
The -E flag enables "extended" regular expressions, which are a lot closer to the syntax used in Perl, JavaScript, and most other modern regex engines. With -E you don't need to have a backslash; it's simply +.
echo "Hello World, I am an example-text" | sed -E 's#[^a-z0-9]+#.#ig'
From https://www.regular-expressions.info/posix.html:
POSIX or "Portable Operating System Interface for uniX" is a collection of standards that define some of the functionality that a (UNIX) operating system should support. One of these standards defines two flavors of regular expressions. Commands involving regular expressions, such as grep and egrep, implement these flavors on POSIX-compliant UNIX systems. Several database systems also use POSIX regular expressions.
The Basic Regular Expressions or BRE flavor standardizes a flavor similar to the one used by the traditional UNIX grep command. This is pretty much the oldest regular expression flavor still in use today. One thing that sets this flavor apart is that most metacharacters require a backslash to give the metacharacter its flavor. Most other flavors, including POSIX ERE, use a backslash to suppress the meaning of metacharacters. Using a backslash to escape a character that is never a metacharacter is an error.
A BRE supports POSIX bracket expressions, which are similar to character classes in other regex flavors, with a few special features. Shorthands are not supported. Other features using the usual metacharacters are the dot to match any character except a line break, the caret and dollar to match the start and end of the string, and the star to repeat the token zero or more times. To match any of these characters literally, escape them with a backslash.
The other BRE metacharacters require a backslash to give them their special meaning. The reason is that the oldest versions of UNIX grep did not support these. The developers of grep wanted to keep it compatible with existing regular expressions, which may use these characters as literal characters. The BRE a{1,2} matches a{1,2} literally, while a\{1,2\} matches a or aa. Some implementations support \? and \+ as an alternative syntax to \{0,1\} and \{1,\}, but \? and \+ are not part of the POSIX standard. Tokens can be grouped with \( and \). Backreferences are the usual \1 through \9. Only up to 9 groups are permitted. E.g. \(ab\)\1 matches abab, while (ab)\1 is invalid since there's no capturing group corresponding to the backreference \1. Use \\1 to match \1 literally.
POSIX BRE does not support any other features. Even alternation is not supported.
(Emphasis mine.)
So my real question: is there any regex definition or implementation that does not treat either x+ or x\+ the same as xx*.
I can't think of any real world language or tool that supports neither + nor \+.
In the formal mathematical definition of regular expressions there are commonly only three operations defined:
Concatenation: AB matches A followed by B.
Alternation: A|B matches either A or B.
Kleene star: R* matches 0 or more repetitions of R.
These three operations are enough to give the full expressive power of regular expressions†. Operators like ? and + are convenient in programming but not necessary in a mathematical context. If needed, they are defined in terms of the others: R? is R|ε and R+ is RR*.
† Mathematically speaking, that is. Features like back references and lookahead/lookbehind don't exist in formal language theory. Those features add additional expressive power not available in mathematical definitions of regular expressions.
In some traditional sed implementations, you have to enable "extended" regular expressions to get support for + to mean "one or more."
For evidence of this, see: sed plus sign doesn't work

Regular expression [:digit:] and [[:digit]] difference

I am currently learning regular expression. I met a problem that I can't find answer on stackoverflow and I hope someone can help find the answer.
I use vim in mac OS system and vim shows the line.
If the file "regular_expression.txt" is:
"Open Source" is a good mechanism to develop programs.
You are the No. 1.
Then
grep -n '[:lower:]' regular_expression.txt
will return
1:"Open Source" is a good mechanism to develop programs.
2:You are the No. 1.
The command
grep '[[:lower:]]' regular_expression.txt
will return
2:You are the No. 1.
I can't understand the above difference because it seems to me that [:lower:] is a set of lower characters. [[:lower:]] should be the same as [:lower:]. It is also confusing that in the first case where [:lower:] is used, all the lines in the file are returned.
POSIX character classes must be wrapped in bracket expressions.
The [:lower:] pattern is a positive bracket expression that matches a single char, :, l, o, w, e or r.
The [[:lower:]] pattern is a positive bracket expression that matches any char that is matched with the [:lower:] POSIX character class (that matches any lowercase letters).
See grep manual:
certain named classes of characters are predefined within bracket expressions... Note that the brackets in these class names are part of the symbolic names, and must be included in addition to the brackets delimiting the bracket expression.
If you mistakenly omit the outer brackets, and search for say, [:upper:], GNU grep prints a diagnostic and exits with status 2, on the assumption that you did not intend to search for the nominally equivalent regular expression: [:epru].

What's the difference between () and [] in a regex?

Let's say:
/(a|b)/ vs /[ab]/
There's not much difference in your above example (in most languages). The major difference is that the () version creates a group that can be backreferenced by \1 in the match (or, sometimes, $1). The [] version doesn't do this.
Also,
/(ab|cd)/ # matches 'ab' or 'cd'
/[abcd]/ # matches 'a', 'b', 'c' or 'd'
() in regular expression is used for grouping regular expressions, allowing you to apply operators to an entire expression rather than a single character. For instance, if I have the regular expression ab, then ab* refers to an a followed by any number of bs (for instance, a, ab, abb, etc), while (ab)* refers to any number of repetitions of the sequence ab (for instance, the empty string, ab, abab, etc). In many regular expression engines, () are also used for creating references that can be referred to after matching. For instance, in Ruby, after you execute "foo" =~ /f(o*)/, $1 will contain oo.
| in a regular expression indicates alternation; it means the expression before the bar, or the expression after it. You could match any digit with the expression 0|1|2|3|4|5|6|7|8|9. You will frequently see alternation wrapped in a set of parentheses for the purposes of grouping or capturing a sub-expression, but it is not required. You can use alternation on longer expressions as well, like foo|bar, to indicate either foo or bar.
You can express every regular expression (in the formal, theoretical sense, not the extended sense that many languages use), with just alternation |, kleene closure *, concatenation (just writing two expressions next to each other with nothing in between), and parentheses for grouping. But that would be rather inconvenient for complicated expressions, so several shorthands are commonly available. For instance, x? is just a shorthand for |x (that is, the empty string or x), while y+ is a shorthand for yy*.
[] are basically a shorthand for the alternation | of all of the characters, or ranges of characters, within it. As I said, I could write 0|1|3|4|5|6|7|8|9, but it's much more convenient to write [0-9]. I can also write [a-zA-Z] to represent any letter. Note that while [] do provide grouping, they do not generally introduce a new reference that can be referred to later on; you would have to wrap them in parentheses for that, like ([a-zA-Z])
So, your two example regular expressions are equivalent in what they match, but the (a|b) will set the first sub-match to the matching character, while [ab] will not create any references to sub-matches.
First, when speaking about regexes, it's often important to specify what sort of regexes you're talking about. There are several variations (such as the traditional POSIX regexes, Perl and Perl-compatible regexes (PCRE), etc.).
Assuming PCRE or something very similar, which is often the most common these days, there are three key differences:
Using parenthetical groups, you can check options consisting of more than one character. So /(a|b)/ might instead be /(abc|defg)/.
Parenthetical groups perform a capture operation so that you can extract the result (so that if it matched on "b", you can get "b" back and see that). /[ab]/ does not. The capture operation can be overridden by adding ?: like so: /(?:a|b)/
Even if you override the capture behavior of parentheses, the underlying implementation may still be faster for [] when you're checking single characters (although nothing says non-capturing (?:a|b) can't be optimized as a special case into [ab], but regex compilation may take ever so slightly longer).