'sed: delete n lines after first match

I want to delete N number of lines after the first match in a text file using sed.

(I know most of these questions have been answered with "use awk", but I want to use sed, regardless of how much more powerful it is than awk. It's more a matter of which tool I'm most comfortable with using at the moment, within a certain time constraint)

The furthest I got is this:

sed -i "0,/pattern/{/pattern/,+Nd}" file.txt

The thought is that 0, denotes the first occurrence, where the curly brackets search the first line for the pattern, and deletes N lines after that occurence

sed


Solution 1:[1]

Try

sed '/pattern/{N;N;N;N;N;N;N;d;}' file.txt

The 0, construct and the relative line number addressing you tried to use are specific to GNU sed. Portable sed does not have these facilities.

This will remove the next six lines after every match. If you only want to remove the first occurrence and leave the rest of the file unchanged, maybe add a separate loop to simply print all remaining lines.

The problem with your attempt is that 0,/pattern/ restricts matching to the lines up through the first occurrence of /pattern/ but then that's the end of the range, so anything selected by this expression cannot operate on lines outside of that range.

Solution 2:[2]

Assuming your shell is bash (the question originally had a bash tag):

n=3
sed -f <(printf -v nsp '%*s' $n; printf '/%s/{x;/./!{s/^/./;h;%sd;};x;}\n' 'pattern' "${nsp// /N;}") file

Note that n is variable (3 is just an instance) and constructed sed script is not GNU specific.

Solution 3:[3]

This might work for you (GNU sed):

sed '0,/pattern/{//{:a;N;s/\n/&/N;Ta;d}}' file

Deletes the line containing pattern and then N lines after it once only.

Alternative:

sed '/pattern/{x;//{x;b};x;h;:a;N;s/\n/&/N;Ta;d}' file

N.B. The N following the substitution command refers to the nth occurrence of a newline in the pattern space.

Solution 4:[4]

UPDATE 1 : Example where sed solution above does not meet objective universally:

cmd='/5=P$/{N;N;N;N;N;N;d;}'
echo "\n input \${b} :: \n\n———————\n" \
     "${b}\n--------------\n\n sed "    \
     "commands :: \n\n--------------\n " \
     "${cmd}\n--------------\n\n GNU sed "\
     "::\n\n$( gsed "${cmd}" <<< "${b}" )" \
     "\n\n BSD sed ::\n\n$( sed "${cmd}" <<< "${b}" )\n\n"

 input ${b} :: 

--------------
    84  77138=48001=P
    85  77138=48035=P
    86  77138=78118=P
    87  77138=79248=P
--------------

 sed commands :: 

--------------
 /5=P$/{N;N;N;N;N;N;d;}
--------------

 GNU sed ::

    84  77138=48001=P
    85  77138=48035=P
    86  77138=78118=P
    87  77138=79248=P

 BSD sed ::

    84  77138=48001=P

For unknown reasons, when the input lacks sufficient rows past the pattern,

  • this solution works on BSD sed,
  • but totally fails on GNU sed.

============================

Is sed a must have requirement ? You can also do one-liners with awk :

(it's intentionally verbose to showcase exactly what the lines matched and skipped look like) :

# gawk profile, created Thu Apr 28 18:36:55 2022

# BEGIN rule(s)

BEGIN {
 1      printf "\n\t N :: %.f :: FS i.e. "\
               "pattern :: %*s\n\n", N = +N, ++__, FS = pattern
}

# Rule(s)

87  NF *= -(_+=(_= __<NF ? -__-N :_)^!__)<+_ { # 45
45      print
}


     1  77138=501=A
     2  77138=3413=A
     3  77138=3414=A
     4  77138=8624=A
     5  77138=19572=A
     6  77138=22220=A
     7  77138=23670=A
     8  77138=25413=A
     9  77138=26351=A
    10  77138=27340=A
    11  77138=29288=A
    12  77138=121060=A
    13  77138=123028=A
    14  77138=132081=A
    15  77138=135789=A
    16  77138=154341=A
    17  77138=155876=A
    18  77138=170871=A
    19  77138=178562=A
skipped ::     20   77138=185367=A
skipped ::     21   77138=196718=A
skipped ::     22   77138=196985=A
skipped ::     23   77138=200012=A
skipped ::     24   77138=207162=A
skipped ::     25   77138=228289=A
skipped ::     26   77138=244747=A
skipped ::     27   77138=284795=A
skipped ::     28   77138=294579=A
skipped ::     29   77138=299765=A
skipped ::     30   77138=317856=A
skipped ::     31   77138=318815=A
    32  77138=324570=A
    33  77138=408049=A
    34  77138=514403=A
    35  77138=1647865=A
    36  77138=1738771=A
    37  77138=3217183=A
skipped ::     38   77138=3222837=A
skipped ::     39   77138=3235292=A
skipped ::     40   77138=14957980=I
skipped ::     41   77138=1159=M
skipped ::     42   77138=1196=M
skipped ::     43   77138=1251=M
    44  77138=1252=M
    45  77138=4951=M
    46  77138=16740=M
    47  77138=71501=M
skipped ::     48   77138=137=P
skipped ::     49   77138=348=P
skipped ::     50   77138=518=P
skipped ::     51   77138=519=P
skipped ::     52   77138=520=P
skipped ::     53   77138=925=P
    54  77138=1363=P
    55  77138=1483=P
    56  77138=1814=P
    57  77138=2692=P
    58  77138=3540=P
    59  77138=3594=P
    60  77138=3682=P
    61  77138=3869=P
    62  77138=3940=P
skipped ::     63   77138=3977=P
skipped ::     64   77138=4025=P
skipped ::     65   77138=4252=P
skipped ::     66   77138=4396=P
skipped ::     67   77138=9501=P
skipped ::     68   77138=13006=P
    69  77138=18113=P
skipped ::     70   77138=20907=P
skipped ::     71   77138=31936=P
skipped ::     72   77138=34954=P
skipped ::     73   77138=37126=P
skipped ::     74   77138=37482=P
skipped ::     75   77138=40135=P
    76  77138=40206=P
    77  77138=41279=P
    78  77138=41280=P
    79  77138=46140=P
skipped ::     80   77138=46157=P
skipped ::     81   77138=46173=P
skipped ::     82   77138=46218=P
skipped ::     83   77138=47592=P
skipped ::     84   77138=48001=P
skipped ::     85   77138=48035=P
    86  77138=78118=P
    87  77138=79248=P
 

     N :: 5 :: FS i.e. pattern :: [7]=[AP]$

     1  77138=501=A
     2  77138=3413=A
     3  77138=3414=A
     4  77138=8624=A
     5  77138=19572=A
     6  77138=22220=A
     7  77138=23670=A
     8  77138=25413=A
     9  77138=26351=A
    10  77138=27340=A
    11  77138=29288=A
    12  77138=121060=A
    13  77138=123028=A
    14  77138=132081=A
    15  77138=135789=A
    16  77138=154341=A
    17  77138=155876=A
    18  77138=170871=A
    19  77138=178562=A
    32  77138=324570=A
    33  77138=408049=A
    34  77138=514403=A
    35  77138=1647865=A
    36  77138=1738771=A
    37  77138=3217183=A
    44  77138=1252=M
    45  77138=4951=M
    46  77138=16740=M
    47  77138=71501=M
    54  77138=1363=P
    55  77138=1483=P
    56  77138=1814=P
    57  77138=2692=P
    58  77138=3540=P
    59  77138=3594=P
    60  77138=3682=P
    61  77138=3869=P
    62  77138=3940=P
    69  77138=18113=P
    76  77138=40206=P
    77  77138=41279=P
    78  77138=41280=P
    79  77138=46140=P
    86  77138=78118=P
    87  77138=79248=P

more concisely, it would be

mawk -v pattern='[7]=[AP]$' -v N='5' -- '
BEGIN { 
      ++__ 
   FS = pattern 
} NF *= -(_+=(_=__<NF?-__-N:_)^!__) < +_' 

or in awk one-liner style

mawk 'NF*=-(_+=(_=1<NF?-1-N:_)^0)<+_' FS='[7]=[AP]$' N=5

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4