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)

  • 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 '15 at 18:09
  • Yes.. I want the blank lines except the first after "MENU1"... :) – ZEE Sep 30 '15 at 18:31
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.

2

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

  • Note: the [^:] means any character but ':' – herbert Sep 30 '15 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 '15 at 18:00
  • To exclude empty line from the beginning ":MENU1\n\K[^:]*" – Costas Sep 30 '15 at 18:00
  • @ZEE: agreed, but not inside square brackets – herbert Sep 30 '15 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 '15 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
  

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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