11

I have this regex:

(?<=prefix).*$

which returns any character following string "prefix" and it works fine on any online regex engines (e.g. https://regex101.com). The problem is when I use that regex in bash:

grep '(?<=prefix).*$' <<< prefixSTRING

it does not match anything. Why that regex does not work with grep?

  • 10
    This really highlights why regex101 needs a POSIX flavor selector the way it does for JS, Perl/PHP, and Python. I can't count the number of times I've wished for that. – Jared Smith Feb 11 at 18:47
  • 1
  • Also, the .*$ matches any string up to the end-of-line (or end-of-string), not just any one character. – ilkkachu Feb 12 at 17:24
36

You seem to have defined the right regex, but not set the sufficient flags in command-line for grep to understand it. Because by default grep supports BRE and with -E flag it does ERE. What you have (look-aheads) are available only in the PCRE regex flavor which is supported only in GNU grep with its -P flag.

Assuming you need to extract only the matching string after prefix you need to add an extra flag -o to let know grep that print only the matching portion as

grep -oP '(?<=prefix).*$' <<< prefixSTRING

There is also a version of grep that supports PCRE libraries by default - pcregrep in which you can just do

pcregrep -o '(?<=prefix).*$' <<< prefixSTRING

Detailed explanation on various regex flavors are explained in this wonderful Giles' answer and tools that implement each of them

37

Regular expressions come in many different flavours. What you are showing is a Perl-like regular expression (PCRE, "Perl Compatible Regular Expression").

grep does POSIX regular expressions. These are basic regular expressions (BRE) and extended regular expressions (ERE, if grep is used with the -E option). See the manual for re_format or regex or whatever similar manual your grep manual refers to on your system, or the POSIX standard texts that I just linked to.

If you use GNU grep, you would be able to use Perl-like regular expressions if you used grep with the GNU grep-specific -P option.

Also note that grep returns lines by default, not substrings from lines. Again, with GNU grep (and some other grep implementations), you may use the -o option to get only the bit(s) that matches the given expression from each line.

Note that both -P and -o are non-standard extensions the POSIX specification of grep.

If you are not using GNU grep, then you may use sed instead to get the bit between the string prefix and the end of the line:

sed -n 's/.*prefix\(.*\)/\1/p' file

What this does is to only print the lines that sed manages to apply the given substitution to. The substitution will replace the whole line that matches the expression (which is a BRE), with the piece of it that occurs after the string prefix.

Note that if there are several instances of prefix on a line, the sed variation would return the string after the last one, while the GNU grep variation would return the string after the first one (which would include the other instances of prefix).

The sed solution would be portable to all Unix-like systems.

6

As the other answers have stated, grep does not use a regex flavour with lookbehinds (by default with GNU grep, or not at all with other versions).

If you find yourself unable to use GNU grep or pcregrep, you can use perl if you have it.

The command line equivalent with perl would be:

perl -ne 'print if /(?<=prefix).*$/' <<< prefixSTRING

You put the desired regex between the slashes. As you are using Perl, this uses Perl's regex flavour.

  • or print "$&\n" if ... if they want to output only the part after the prefix – ilkkachu Feb 12 at 17:25

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.