sed or awk: delete n lines following a pattern

UnixSedAwk

Unix Problem Overview


How would I mix patterns and numeric ranges in sed (or any similar tool - awk for example)? What I want to do is match certain lines in a file, and delete the next n lines before proceeding, and I want to do that as part of a pipeline.

Unix Solutions


Solution 1 - Unix

I'll have a go at this.

To delete 5 lines after a pattern (including the line with the pattern):

sed -e '/pattern/,+5d' file.txt

To delete 5 lines after a pattern (excluding the line with the pattern):

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

Solution 2 - Unix

Without GNU extensions (e.g. on macOS):

To delete 5 lines after a pattern (including the line with the pattern)

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

Add -i '' to edit in-place.

Solution 3 - Unix

Simple awk solutions:

Assume that the regular expression to use for finding matching lines is stored in shell variable $regex, and the count of lines to skip in $count.

If the matching line should also be skipped ($count + 1 lines are skipped):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

If the matching line should not be skipped ($count lines after the match are skipped):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Explanation:

  • -v regex="$regex" -v count="$count" defines awk variables based on shell variables of the same name.
  • $0 ~ regex matches the line of interest
    • { skip=count; next } initializes the skip count and proceeds to the next line, effectively skipping the matching line; in the 2nd solution, the print before next ensures that it is not skipped.
    • --skip >= 0 decrements the skip count and takes action if it is (still) >= 0, implying that the line at hand should be skipped.
      • { next } proceeds to the next line, effectively skipping the current line
  • 1 is a commonly used shorthand for { print }; that is, the current line is simply printed
    • Only non-matching and non-skipped lines reach this command.
    • The reason that 1 is equivalent to { print } is that 1 is interpreted as a Boolean pattern that by definition always evaluates to true, which means that its associated action (block) is unconditionally executed. Since there is no associated action in this case, awk defaults to printing the line.

Solution 4 - Unix

This might work for you:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21

Solution 5 - Unix

Using Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$

Solution 6 - Unix

This solution allows you to pass "n" as a parameter and it will read your patterns from a file:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

The file named "-" means stdin for awk, so this is suitable for your pipeline

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMartin DeMelloView Question on Stackoverflow
Solution 1 - UnixdogbaneView Answer on Stackoverflow
Solution 2 - UnixthakisView Answer on Stackoverflow
Solution 3 - Unixmklement0View Answer on Stackoverflow
Solution 4 - UnixpotongView Answer on Stackoverflow
Solution 5 - Unixstack0114106View Answer on Stackoverflow
Solution 6 - Unixglenn jackmanView Answer on Stackoverflow