4

I have the following text in the data.txt file

:MENU1
0. public
1. admin
2. webmail

:SYNTAX
! opt1, ... :

:ERROR1
Error #1, blah... blah.. blah...
Please do ...

:ERROR2
Error #2 ...

and I want to use a regular expression (PERL syntax) to extract the part from :MENU1 to the next first :, but dropping MENU1 and the last : from the result.

Been trying several regex's but in the closest solution I got I can't put the 'greedy' option to work and cant't discard the last ":"

grep -Poz "^:MENU1\K[\w\W]*:"

this works with grep ...
but brings all the text until the last ":" ...
I want only until the next first ":" after :MENU1:

0. public
1. admin
2. webmail
 

(note the final blank line)

2
  • Do you also want the blank lines? There's a \n after :MENU1 and before the first :, should those be included? Please edit your question and show us your desired output.
    – terdon
    Sep 30, 2015 at 18:09
  • Yes.. I want the blank lines except the first after "MENU1"... :)
    – ZEE
    Sep 30, 2015 at 18:31

3 Answers 3

4

The pattern *: will match everything until the last :. To stop at the next : you need *?:. E.g.:

% grep -Poz '^:MENU1\K[\w\W]*?:' data.txt 

0. public
1. admin
2. webmail

:

You can strip the first line by matching the newline before your \K. E.g.:

% grep -Poz '^:MENU1\n\K[\w\W]*?:' data.txt 
0. public
1. admin
2. webmail

:

To eat the empty line and the : you can match and discard that text. E.g.:

% grep -Poz '^:MENU1\n\K[\w\W]*?(?=\n+:)' data.txt 
0. public
1. admin
2. webmail

next we can simplify your character class, to match on anything but ::

% grep -Poz '^:MENU1\n\K[^:]*?(?=\n+:)' data.txt 
0. public
1. admin
2. webmail

And finally we can rewrite the initial part of the match:

% grep -Poz '(?<=:MENU1\n)[^:]*?(?=\n+:)' data.txt 
0. public
1. admin
2. webmail

This is similar to what @terdon came up with, but this takes care of the blank lines without another call to grep.

This final regex makes use of look-around assertions. The (?<=pattern) is a look-behind assertion that lets you match the pattern but not include it as part of the output. The (?=pattern) is a look-ahead assertion and lets us match on the trailing pattern without including it in the output.

0
2

What about: grep -Poz "^:MENU1\K[^:]*"?

6
  • Note: the [^:] means any character but ':'
    – herbert
    Sep 30, 2015 at 17:59
  • with -P (PERL) flag the ^ means the start of the line...<br> and there the ^is working OK... <br> only problem is the greed thing not working
    – ZEE
    Sep 30, 2015 at 18:00
  • To exclude empty line from the beginning ":MENU1\n\K[^:]*"
    – Costas
    Sep 30, 2015 at 18:00
  • @ZEE: agreed, but not inside square brackets
    – herbert
    Sep 30, 2015 at 18:03
  • 1
    @ZEE: default perl behavior is to match the longest string, if you want a greedy pattern you have to use the '?' modifier like grep "^:MENU1\K[\w\W]*?:"
    – herbert
    Sep 30, 2015 at 18:08
2

@Herbert's solution is probably the simplest, but you could also use lookarounds:

$ grep -Poz '(?<=:MENU1\n)[^:]*' file 
0. public
1. admin
2. webmail
  

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .