0

I have a file like this:

Lorem ipsum dolor sit amet.
Consectetur adipiscing elit.
MATCH1 Quis autem vel!
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
MATCH1 Eum iure reprehenderit?
MATCH2 Qui in ea voluptate
Ut enim ad minim veniam.
Quis nostrud exercitation
TARGET
Sed ut perspiciatis unde.
MATCH1 Nemo enim ipsam voluptatem quia voluptas?
MATCH2 Sit aspernatur aut odit aut fugit.
Omnis iste natus error sit.
Voluptatem accusantium doloremque laudantium
MATCH1 Ut enim ad minima veniam.
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam
TARGET
Ut enim ad minima veniam.

How would I find all lines that start with MATCH (there can be one or many, but grouped together if there is more than one) and move them above the next occurrence of TARGET (always the same word, only one occurrence at a time), and iteratively repeat this process all the way down the file? The processed file would like this:

Lorem ipsum dolor sit amet.
Consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam.
Quis nostrud exercitation
MATCH1 Quis autem vel!
MATCH1 Eum iure reprehenderit?
MATCH2 Qui in ea voluptate
TARGET
Sed ut perspiciatis unde.
Omnis iste natus error sit.
Voluptatem accusantium doloremque laudantium
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam
MATCH1 Nemo enim ipsam voluptatem quia voluptas?
MATCH2 Sit aspernatur aut odit aut fugit.
MATCH1 Ut enim ad minima veniam.
TARGET
Ut enim ad minima veniam.

Any GNU/Linux commands are fine, preferably not vi or Emacs.

Thanks a lot!

2
  • What have you tried? What didn't work? What did you get? What did you expect? What doesn't work with your code and where is it?
    – Toto
    Commented Jan 10, 2020 at 17:59
  • @Toto, nothing yet, don't even know where to start.
    – rahim123
    Commented Jan 10, 2020 at 18:46

1 Answer 1

1

sed with its "hold space" functionality seems good for the job.

<file sed '
   /^MATCH/ {H;d}
   /^TARGET$/ {H;s/.*//;x;s/\n//}
   '

It works like this:

  • Any line of input that doesn't start with MATCH and doesn't consist of TARGET triggers no explicit commands and gets printed because it's the default behavior upon reaching the end of a script.
  • A line that starts with MATCH

    • gets appended to the hold space (H);
    • next the pattern space is deleted and a new cycle starts (next line is read, the script starts over) without printing anything (d).
  • A line that consists of TARGET triggers the following:

    • The line itself gets appended to the hold space (H). At this moment the hold space holds an empty line (because its initially empty content became an empty line after the fist H), zero or more lines with MATCH and exactly one line with TARGET.
    • The whole pattern space is replaced by nothing (s/.*//).
    • The pattern space and the hold space are swapped (x). At this moment the hold space is empty (ready to collect MATCHes anew) and the pattern space contains an empty line, few MATCHes maybe and one TARGET.
    • The unwanted empty line in the pattern space is just a newline character at the very beginning. It needs to be removed and this is what the next s does.
    • Then the script continues and the end of it is reached. The pattern space gets printed because it's the default behavior upon reaching the end.

So basically MATCHes are collected in the hold space and printed upon TARGET.

Notes:

  • ^MATCH means MATCH must be at the very beginning of the line. MATCH with leading spaces (or tabs etc.) won't match. ^TARGET$ requires no leading nor trailing spaces (or tabs etc.). If this is not right, adjust these patterns.
  • MATCHes are collected even if not grouped together.
  • MATCH(es) occurring after the last TARGET (or if there is no TARGET at all) will not be printed because there is no TARGET that would trigger printing the hold space that collected them.
0

You must log in to answer this question.

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