Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete n1 previous lines and n2 lines following with respect to a line containing a pattern

Tags:

bash

unix

sed

sed -e '/XXXX/,+4d' fv.out

I have to find a particular pattern in a file and delete 5 lines above and 4 lines below it simultaneously. I found out that the line above removes the line containing the pattern and four lines below it.

sed -e '/XXXX/,~5d' fv.out

In sed manual it was given that ~ represents the lines which is followed by the pattern. But when i tried it, it was the lines following the pattern that was deleted.

So, how do I delete 5 lines above and 4 lines below a line containing the pattern simultaneously?

like image 414
Population Xplosive Avatar asked Feb 25 '12 08:02

Population Xplosive


4 Answers

One way using sed, assuming that the patterns are not close enough each other:

Content of script.sed:

## If line doesn't match the pattern...
/pattern/ ! { 

    ## Append line to 'hold space'.
    H   

    ## Copy content of 'hold space' to 'pattern space' to work with it.
    g   

    ## If there are more than 5 lines saved, print and remove the first
    ## one. It's like a FIFO.
    /\(\n[^\n]*\)\{6\}/ {

        ## Delete the first '\n' automatically added by previous 'H' command.
        s/^\n//
        ## Print until first '\n'.
        P   
        ## Delete data printed just before.
        s/[^\n]*//
        ## Save updated content to 'hold space'.
        h   
    } 

### Added to fix an error pointed out by potong in comments.
### =======================================================
    ## If last line, print lines left in 'hold space'.
    $ { 
        x   
        s/^\n//
        p   
    } 
### =======================================================


    ## Read next line.
    b   
}

## If line matches the pattern...
/pattern/ {

    ## Remove all content of 'hold space'. It has the five previous
    ## lines, which won't be printed.
    x   
    s/^.*$//
    x   

    ## Read next four lines and append them to 'pattern space'.
    N ; N ; N ; N 

    ## Delete all.
    s/^.*$//
}

Run like:

sed -nf script.sed infile
like image 96
Birei Avatar answered Nov 14 '22 08:11

Birei


The idea is to read 5 lines without printing them. If you find the pattern, delete the unprinted lines and the 4 lines bellow. If you do not find the pattern, remember the current line and print the 1st unprinted line. At the end, print what is unprinted.

sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out

Of course, this only works if you have one occurrence of your pattern in the file. If you have many, you need to read 5 new lines after finding your pattern, and it gets complicated if you again have your pattern in those lines. In this case, I think sed is not the right tool.

like image 24
jfg956 Avatar answered Nov 14 '22 08:11

jfg956


This might work for you:

sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file

or this:

awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file

a more efficient sed solution:

sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file
like image 20
potong Avatar answered Nov 14 '22 10:11

potong


If you are happy to output the result to a file instead of stdout, vim can do it quite efficiently:

vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile

or

vim -c 'g/pattern/-5,+4d' -c 'x' infile

to edit the file in-place.

like image 34
Robbie Clarken Avatar answered Nov 14 '22 10:11

Robbie Clarken