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
/pattern/,+5
defines a range, which starts with a line containing "pattern" (/pattern/
) and ends 5 lines later (+5
). The last character d
is a command to run on each line in that range, which is "delete". In the second recipe, instead of matching a range, it matches just at the line containing the pattern (/pattern/
) and then runs a series of commands: {n;N;N;N;N;d}
, which basically prints the next line (n
) and then reads and finally discards the next 4 lines (N;N;N;N;d
).
sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
something
do: sed -E '/^something$/,$d'
, where -E
is the POSIX portability extended regex.
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.
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
{ 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.
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.
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
pattern_number.txt
is a 2-column file containing the pattern to match in the 1st column, and in the 2nd the number of lines to skip. The first sed
command transforms the file into a sed
script that performs the corresponding matching and skipping; that script is provided via -f
and stdin (-
) to the 2nd sed
command. The 2nd sed
command operates on a sample ad-hoc input file formed from the output of seq 21
to demonstrate that it works.
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
$
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
Success story sharing
+N
pattern is a GNU extension. Change the firstn
to anN
in your second example to make it include the line with the pattern.