I know that I can print the "next" line of a match with grep
, with -A1
, but what about the inverse of that? I mean, "hide the next line after a filter match".
Example:
a
b
c
d
e
cat letters.txt | grep -A1 -v "c"
<-- hide "c" and "d"
I know that I can print the "next" line of a match with grep
, with -A1
, but what about the inverse of that? I mean, "hide the next line after a filter match".
Example:
a
b
c
d
e
cat letters.txt | grep -A1 -v "c"
<-- hide "c" and "d"
grep
by itself doesn't (usually) offer this functionality, but it's easy enough with Awk or sed
.
awk '/c/ { skip=1; next }
skip { skip=0; next } 1' letters.txt
You could do this with sed
like
sed -ne '/c/{n;d;}; p' letters.txt
This will read the file letters.txt
. Any line that does not match a c
will be printed, if a c
is matched, do not print that, and also go to the next line and delete that from the output
With GNU sed to make it easier to get N lines it can be done like
sed -ne '/c/,+1d; p;' letters.txt
and you can change the 1
to whatever number you like. Also, you could add the -i
flag to sed
to make it modify the file in place if you wanted to do that
This answer looks at general solutions with variable skip count.
If you have GNU sed
, Eric Renouf's elegant answer is the simplest solution.
It can be slightly simplified to sed '/c/,+1d'
, or, generalized:
regex='c' count=2
sed "/${regex}/,+${count}d" letters.txt # skips c, d, e
The +<n>
form of a context address to match the <n>
-th line from the start of the range (and thus the start of the range through the following <n>
lines) is a GNU sed
extension.
Note that $count
+1 lines are skipped: the line matching the regex + $count
lines after.
For portability and for more flexibility in general (such as not skipping the matching line itself), use a generalized version of tripleee's helpful awk
answer:
awk -v regex='c' -v count=2 \
'$0 ~ regex { skip=count; next } --skip >= 0 { next } 1' letters.txt # skips c, d, e
Note that $count
+1 lines are skipped: the line matching the regex + $count
lines after.
-v regex='c' -v count=2
defines awk
variables with the specified values.$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 line1
is a commonly used shorthand for { print }
; that is, the current line is simply printed
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.As a POSIX-compliant shell function:
# SYNOPSIS
# matchAndSkip regex skipCount [file]
matchAndSkip() {
awk -v regex="$1" -v count="$2" \
'$0 ~ regex { skip=count; next } --skip >= 0 { next } 1' "${3-/dev/stdin}"
}
Using awk you can also do:
awk -v s='c' '$0 ~ s {getline;next} 1' letters.txt
a
b
e