1

I need to print the text between two patterns without keeping in mind their place as they are found randomly across the file. either in the same line or not in the same line or a text occur between them

Patterns are : <abc> and </abc>

example :

aslkdjas<abc>aaaa</abc><abc>bbbb</abc>sdkljasdl<abc>
cccc
dddd</abc>ieurwioeru<abc>eeee</abc>asdasd

I need an output like the following or to be comma separated whatever happen in this file to display the values between two patterns:

aaaa
bbbb
cccc
dddd
eeee
  • Is this in fact an XML document? – Kusalananda Oct 27 '18 at 15:58
1

I don't recommend to parse any functional code with text-processing tools. They are simply designed for parsing only human language and sooner or later you will stuck with the problem that cannot be solve. Use dedicated tools instead (html interpreter, c++ compiler, etc.)

With that being said in this case you can try pcregrep:

pcregrep -Mo '<abc>\K(.|\n)*?(?=</abc>)' file

The result is

aaaa
bbbb

cccc
dddd
eeee

Yes, there is new line between bbbb and cccc because in the original file we have new line. Of course you can pipe the output to remove whitespaces if you want to (with tr, sed or whatever), but as I've said: in the real life examples you may encounter more unexpected results.

  • I forgot to add a link to proper description of "don't parse html with regex": stackoverflow.com/a/1732454/4488514 – jimmij Oct 27 '18 at 23:46
  • How could we differentiate between newlines that are "field separators" from those that were included in the original file?. Shouldn't you use a comma (,) as requested? – Isaac Nov 3 '18 at 5:44
0

For that simple case, try

sed ':L1; N; $bL2; bL1; :L2; s#<abc>#^A#g; s#^[^^A]*^A##; s#</abc>[^^A]*^A#\n#g; s#</abc>.*$##; ' file
aaaa
bbbb

cccc
dddd
eeee

Collect all lines into pattern space, replace leading pattern with ^A, remove BOL to first ^A, replace strings between patterns with <new line>, remove pattern until EOL, print.

  • The sequence ':L1; N; $bL2; bL1; :L2; could be replaced by ':L1; N; $!bL1;, in fact, this is shorter/simpler: 'H;$!d;g; (capture everything in the hold buffer, on the last line "get it"). – Isaac Nov 3 '18 at 5:02
  • How could we differentiate between newlines that are "field separators" from those that were included in the original file?. Shouldn't you use a comma (,) as requested? – Isaac Nov 3 '18 at 5:16
0

sed

A sed solution is to convert the patterns and to two other characters that are not used inside the file anywhere else. That will convert the problem to the general one of extracting between two single characters.

  1. First, convert each pattern to single characters:

    sed 'H;$!d;x; s##^A#g; s##^B#g;' file

    That is assuming that you typed Ctrl-V Ctrl-A for each ^A and similarly for ^B.

    The initial H;$!d;x; is to capture the whole file in the pattern space. That means:

    • Hold every line
    • erase the pattern space (and return to the beginning) d
    • if it is not the last line $!
    • get all the lines stored in the hold space x. (could be g, but x needs less memory as the whole file is not copied from the hold space to the pattern space).
  2. The general process to extract a pattern between two single characters (assume x and y here) is:

    sed 's#^[^x]x##; s#y[^y]$##; s#y[^x]*x#,#g;'

    That is:

    • remove leading characters before the first (^) x.
    • remove the trailing characters after the last ($) y.
    • Convert characters between y and x to a delimiter (comma (,) in this case).

All together:

$ sed 'H;$!d;x; s#<abc>#^A#g; s#</abc>#^B#g;' s#^[^^A]*^A##; s#^B[^^B]*$##; s#^B[^^A]*^A#,#g;' file
aaaa,bbbb,
cccc
dddd,eeee

grep

It could be done with (GNU) grep but it needs the help of paste to put the commas (only) in the right places:

$ grep -ozP '(?s)<abc>\K.*?(?=</abc>)' file | paste -zsd ','; echo
aaaa,bbbb,
cccc
dddd,eeee

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.