bash: sed to extract all lines from string until different string

Viewed 93

I have the following text input.txt file

abc xyz 123
  this is text
  i dont need
abc def 123
  this is text
  i want to keep
and this also
{
contains
}
these lines

 xyz
xyz

abc def ppp
  this is also text
  i want to keep
abc fff
  this is text
  i dont need

I want to keep only the text where the section starts with abc def like below

abc def 123
  this is text
  i want to keep
and this also
{
contains
}
these lines

 xyz
xyz

abc def ppp
  this is also text
  i want to keep

I have the command sed -n "/^abc def/,/^abc fff/p" input.txt and got this:

abc def 123
  this is text
  i want to keep
and this also
abc def ppp
  this is also text
  i want to keep
abc fff

But i got 2 problems:

  • it grabs the line abc fff
  • the marker to stop is not necessary abc fff, it can be abc fgh

Edit: I would like to remove the files from the input file.

3 Answers

This might work for you (GNU sed):

sed -nE '/^\S/{:a;x;/\S/{/^abc def|^\S[^\n]*$/p};$!b;x;/^\S/p;b};H;$ba' file

In essence, print a stanza (an unindented line followed by zero or more indented lines) if it begins abc def or is a single line.

Turn off implicit printing and turn on extended regexp -nE.

If the current line is the first (or only) line of a stanza, switch to the hold space.

If the hold space is not empty and begins abc def or is the only line of a stanza, print it.

If this is not the last line, break and the current line becomes the hold space.

Otherwise, this must be the last line of the file and if it is the first line of stanza print it.

All other lines are appended to the hold space and if it happens to be the last line of the file, the program flow jumps back up and treats it as if the first line of a stanza has been found.


As the input has changed and I believe the delimiter for a stanza is any line beginning abc , a simpler solution:

sed -nE '/^abc def/{:a;p;n;/^abc /!ba;s/^/\n/;D}' file

Print any stanza that begins abc def until another stanza beginning abc , then prepend a newline and invoke the D command, which preserves the current pattern space having removed up and including the first newline.

Though you tagged your question with sed, following gnu-awk should be a good tool to get the job done:

awk -v RS='abc ' '$1 == "def" {print RT $0}' input.txt

abc def 123
  this is text
  i want to keep
and this also
{
contains
}
these lines

 xyz
xyz


abc def ppp
  this is also text
  i want to keep

Code Demo


To store output in another file, use:

awk -v RS='abc ' '$1 == "def" {print RT $0 > "output"}' input.txt

To remove all matched text use:

awk -v RS='abc ' '$1 != "def" {ORS=RT; print $0 > "removed"}' input.txt

With your shown samples and attempts, please try following awk code.

awk '
/^abc/{
  if(found && val){
    print val
  }
  found=val=""
}
/^abc def$/{
  found=1
}
found{
  val=(val?val ORS:"") $0
}
END{
  if(found && val){
    print val
  }
}
'   Input_file

Explanation: Adding detailed explanation for above code.

awk '                        ##Starting awk program from here.
/^abc/{                      ##Checking condition if line starts from abc then do following.
  if(found && val){          ##Checking if found is SET and val is SET then do following.
    print val                ##Printing val here.
  }
  found=val=""               ##Nullifying found and val here.
}
/^abc def$/{                 ##Checking if line starts from abc def then do following.
  found=1                    ##Setting found to 1 here.
}
found{                       ##Checking if found it SET then do following.
  val=(val?val ORS:"") $0    ##Creating val where keeping current line and keep appending it with new line.
}
END{                         ##Starting END block of this awk program from here.
  if(found && val){          ##Checking if found is SET and val is SET then do following.
    print val                ##Printing val here.
  }
}
'   Input_file               ##Mentioning Input_file name here.
Related