7

I need delete a matching line and one previous to it. e.g In file below I need to remove lines 1 & 2.

I tried "grep -v -B 1 "page.of." 1.txt and I expected it to not print the matchning lines and the context.

I tried the How do I delete a matching line, the line above and the one below it, using sed? but could not understand the sed usage.

---1.txt--
**document 1**                         -> 1
**page 1 of 2**                        -> 2

testoing
testing

super crap blah

**document 1**
**page 2 of 2**
Community
  • 1
  • 1
SpH
  • 79
  • 1
  • 2
  • 2
    try [`tac file | sed -e '/foo/,+1d' | tac`](http://stackoverflow.com/a/31227307/2297751) – Jon Jul 05 '15 at 05:10

5 Answers5

14

You want to do something very similar to the answer given

sed -n '
/page . of ./ { #when pattern matches
n #read the next line into the pattern space
x #exchange the pattern and hold space
d #skip the current contents of the pattern space (previous line)
}

x  #for each line, exchange the pattern and hold space
1d #skip the first line
p  #and print the contents of pattern space (previous line)

$ { #on the last line
x #exchange pattern and hold, pattern now contains last line read
p #and print that
}'

And as a single line

sed -n '/page . of ./{n;x;d;};x;1d;p;${x;p;}' 1.txt
Community
  • 1
  • 1
Hasturkun
  • 35,395
  • 6
  • 71
  • 104
4

grep -v -B1 doesnt work because it will skip those lines but will include them later on (due to the -B1. To check this out, try the command on:

**document 1**                         -> 1
**page 1 of 2**                        -> 2

**document 1**
**page 2 of 2**
**page 3 of 2**

You will notice that the page 2 line will be skipped because that line won't be matched and the next like wont be matched.

There's a simple awk solution:

awk '!/page.*of.*/ { if (m) print buf; buf=$0; m=1} /page.*of.*/ {m=0}' 1.txt

The awk command says the following:

If the current line has that "page ... of ", then it will signal that you haven't found a valid line. If you do not find that string, then you print the previous line (stored in buf) and reset the buffer to the current line (hence forcing it to lag by 1)

Foo Bah
  • 25,660
  • 5
  • 55
  • 79
2
grep -vf <(grep -B1 "page.*of" file | sed '/^--$/d') file
bash-o-logist
  • 6,665
  • 1
  • 17
  • 14
1

Not too familiar with sed, but here's a perl expression to do the trick:

cat FILE | perl -e '@a = <STDIN>;
                    for( $i=0 ; $i <= $#a ; $i++ ) { 
                     if($i > 0 && $a[$i] =~ /xxxx/) { 
                       $a[$i] = ""; 
                       $a[$i-1] = "";
                     }
                    } print @a;'

edit:

where "xxxx" is what you are trying to match.

tchrist
  • 78,834
  • 30
  • 123
  • 180
entitledX
  • 670
  • 1
  • 7
  • 13
  • And the Useless Use of Cat should go. – tripleee Sep 11 '11 at 15:21
  • Yes, certain pieces of this solution can be criticized, but the general concept is solid and the example code implements it fairly well. It is easy to understand. Enough to get someone started. And that is the whole point of an answer here. It doesn't need to be perfect. I like this answer. It certainly helped me with a problem similar to the original question. – Keve Nov 15 '17 at 13:54
  • If you want to delete lines containing `xxxx` but nothing more, I would suggest `xxxx\r` and `xxxx\r\n` in case other lines are `xxxxabc`. – Smeterlink Jul 09 '21 at 09:19
0

Thanks, I was trying to use the awk command given by Foo Bah to delete the matching line and the previous one. I have to use it multiple times, so for the matching part I use a variable. The given awk command works, but when using a variable it does not work (i.e. it does not delete the matching & prev. line). I tried:

awk -vvar="page.*of.*" '!/$var/ { if (m) print buf; buf=$0; m=1} /$var/ {m=0}' 1.txt

ni_hao
  • 404
  • 2
  • 5
  • 16