64

Is it possible to use GNU grep to get a matched group from an expression?

Example:

echo "foo 'bar'" | grep -oE "'([^']+)'"

Which would output "'bar'". But I would like to get just "bar", without having to send it through grep one more time (ie. get the matched group). Is that possible?

Torandi
  • 928
  • 1
  • 7
  • 9

3 Answers3

58

You can use sed for this. On BSD sed:

echo "foo 'bar'" | sed -E "s/.*'([^']+)'.*/\\1/"

Or, without the -E option:

sed "s/.*'\([^']\+\)'.*/\1/"

This doesn't work for multiline input. For that you need:

sed -n "s/.*'\([^']\+\)'.*/\1/p"
slhck
  • 223,558
  • 70
  • 607
  • 592
jtbandes
  • 8,800
  • 3
  • 45
  • 70
  • Thanks, had forgotten about sed. But to clarify, sed doesn't take the argument -E.. – Torandi Jul 22 '09 at 23:36
  • 1
    Hm, it does on my machine (Mac OS X). Upon further examination, in the man page: "The -E, -a and -i options are non-standard FreeBSD extensions and may not be available on other operating systems." – jtbandes Jul 22 '09 at 23:40
  • Okay, it doesn't on mine (debian), what is -E supposed to do? – Torandi Jul 22 '09 at 23:42
  • 1
    It's similar to `grep`'s -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." – jtbandes Jul 22 '09 at 23:44
  • 1
    -r seems to to that for me. – Torandi Jul 22 '09 at 23:46
  • And I don't have -r. Oh well! – jtbandes Jul 22 '09 at 23:49
  • 1
    @jtbandes: You don't need the extended features for this expression.. I just requires 3 escape characters for `( ) +` use `\( \) \+`: This is effectively the same: `sed "s/.*'\([^']\+\)'.*/\1/"` – Peter.O Jan 11 '12 at 01:38
  • 3
    This doesn't work for multiline input. For that you need: `sed -n "s/.*'\([^']\+\)'.*/\1/p"` – phreakhead Oct 10 '12 at 18:15
36

While grep can't output a specific group, you can use lookahead and behind assertions to achieve what your after:

echo "foo 'bar'" | grep -Po "(?<=')[^']+(?=')"

Aldrik
  • 906
  • 9
  • 5
  • 11
    `grep -P` is not available on all platforms. But if it is, using lookahead/behind is a very nice way of solving the problem. – Sébastien Jun 13 '12 at 13:16
  • 1
    Is grep intelligent with the look-behind assertions? How does it perform with long look-behinds? Is it integrating the look-behinds into some sort of ["suffix tree"](http://en.wikipedia.org/wiki/Suffix_tree) with the rest of the regex? – Ross Rogers Oct 01 '12 at 18:33
  • 1
    Literally an article I accidentally found related to this example: https://franklingu.github.io/programming/2016/11/19/grep-and-show-capture-group-only/ – Artfaith Jun 08 '21 at 07:16
9

You can use \K to reset and discard the left hand match text along with a lookahead which is not added to the match text:

$ echo "foo 'bar'" | grep -oP "'\K[^']+(?=')"
bar

GNU grep only.

drewk
  • 1,194
  • 7
  • 13