Bash event not found trying to match and exclude parenthesis in grep

bashgrepquotingregular expressionshell

In a very long line I'll summarize with:

(foo),(bar,baz(word,right),(end)

I want to print only:

      (bar,baz(word,right

To match the second parenthesis, I exclude the word that follows the third one:

$ grep -oP "\\(.*(?!word).*right"

but Bash interprets the exclamation mark:

-bash: !word: event not found

Protecting the exclamation mark with single quote fails with grep: missing )

$ grep -oP '\\(.*(?!word).*right'
$ grep -oP '\\((?!word)(.*right)'

Protecting the exclamation mark with backslash fails with grep: unrecognized character after (? or (?-

Any idea?

Note: -P is for Perl regex and -o is to print only the matching part of a line

Best Answer

The rules are different for single quotes versus double quotes.

For the reason you show, double quotes can't be used reliably in bash, because there's no sane way to escape an exclamation mark.

$ grep -oP "\\(.*(?!word).*right"
bash: !word: event not found

$ grep -oP "\\(.*(?\!word).*right"
grep: unrecognized character after (? or (?-

The second is because bash passes through \! rather than ! to grep. Showing this:

$ printf '%s' "\!"
\!

When you tried single quotes, the double backslash doesn't mean an escaped backslash, it means two backslashes.

$ printf '%s' '\\(.*(?!word).*right'
\\(.*(?!word).*right

Inside single quotes, everything is literal, and there are no escapes, so the way to write the regular expression you're trying is:

$ grep -oP '\(.*(?!word).*right'
Related Question