Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

share|improve this question
1  
grep -o '(b[^)]*' works in that case. Your attempted usage of the negative look ahead operator doesn't make sense. What exactly do you want to match? –  Stéphane Chazelas Mar 31 at 14:03
2  
use set +H to turn off history expansion, set -H to turn it back on. –  glenn jackman Mar 31 at 14:15
    
@glennjackman Or just use proper quoting: single quotes in this case. (Which gets to the next step that the regex isn't syntactically correct.) –  Gilles Mar 31 at 20:06

5 Answers 5

If you want to match from the second open parentheses up until (but not including) the next closing parentheses:

grep -Po '\(.*?\K\([^)]*'

Or portably with sed:

sed -n 's/^[^(]*([^(]*\(([^)]*\).*/\1/p'

To match the right most ( that is not followed by word up to the rightmost right after that:

grep -Po '.*\K\((?!word).*right'
share|improve this 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'
share|improve this answer

You can do it with simple awk:

$ echo '(foo),(bar,baz(word,right),(end)' | awk -F'),' '{print $2}'
(bar,baz(word,right
share|improve this answer

Look for the comma parens before and aft instead:

grep -oP '(?<=,)\(.*(?=\),)'

Example

$ echo '(foo),(bar,baz(word,right),(end)' | grep -oP '(?<=,)\(.*(?=\),)'
(bar,baz(word,right

The lookahead and behind can only look for explicit strings, it cannot find things such as .*.

References

share|improve this answer

If for some reason you cannot use single quotes as suggested in Mikel's answer, you can temporarily turn off history expansion using set +H (turn it back on with set -H), as suggested by glenn jackman in comments.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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