I have a file that contain data like:
- 123
- 456
- 789
I want to delete the above two lines (123 and 456) when I find a match 789. Is it possible to do it with sed or awk? Please Help
awk to the rescue!
awk '{a[NR]=$0} END{for(i=1;i<=NR;i++) if(a[i+1] a[i+2]!~/789/) print a[i]}' test.1
input (test.1)
1. 123
2. 456
3. 789
4. 012
5. 345
6. 678
7. 901
8. 789
9. 111
output
3. 789
4. 012
5. 345
8. 789
9. 111
loading the file in memory (so not the best for huge file)
sed '1h;1!H;$!d
x;/\n[0-9]\{1,\}\.[[:space:]]\{1,\}789$/ s/\(\(\n\)[[:alnum:][:blank:][:punct:]]*\)\{3\}$/\2789/
' YourFile
load the file in memory, check if last line match, if yes remove 2 previous line, print result
This might work for you (GNU sed):
sed ':a;N;s/\n/&/2;Ta;/\n789$/s/.*\n//;P;D' file
Keep a moving window of 3 lines in the pattern space and if the third line is the desired pattern, remove the first two lines.
This script does not load the whole file into memory, unlike some of the other answers, so it's efficient for big files, and this assumes that you have at least 3 lines
#n
1{
N
h
n
:loop
${
/789/! {
x
p
g
}
p
}
$!{
H
g
P
s/^[^\n]*\n//
h
n
b loop
}
}
If you save this as s.sed, you can run
sed -f s.sed file
And it will delete the two lines before the last line if the last line matches 789.
Input:
123
456
789
Output:
789
Input:
abc
123
456
789
Output:
abc
789
Input:
123
456
abc
Output:
123
456
abc
Explanation
The #n
suppresses normal output. On the first line, matched by 1
, we append the next line with N
and copy this to the hold space with h
. Then we go to the next line with n
and then start the :loop
.
If the current line is the last line, which is picked up by $
, then we check if it doesn't match 789, in which case we swap the pattern and hold spaces with x
, print out the new pattern space with p
, then use g
to copy the hold space onto the pattern space. Finally we print out the last line.
If the current line isn't the last line. We append the current line to the hold space with H
, then we copy the hold space to pattern space. We print the first line of the pattern space with P
, and then remove the first line with s/^[^\n]*\n//
. We copy this back to the hold space, go to the next line, and repeat the loop with b loop
.
Using
ed
, the standard editor:ed
will see these commands:Explanation:
will mark all lines from line 3* to the end of the file that match
/789/
; then, for each marked line, it will execute the command:which means: delete the range
-2,.
, i.e., the range that starts two lines above the current one (-2
) and ends here (.
).Then
w
means to write the file. If you want to try it first without writing to the file, but instead print on the terminal (for testing purposes), replace thew
command by,p
andQ
as so:Note.
ed
really edits the file (it's an editor), so it will happily preserve symlinks and permissions, unlike all other methods that rely on tools that are good for streams (sed
andawk
).Note. If your pattern appears in the first two lines, it will be preserved (though, this can be handled by adding some extra commands); if your pattern appears within a range of 2 lines, you might have some surprises, e.g.,
will yield an empty file, and
you'll get an error.
*The range starts at line 3, just to prevent errors in case your pattern appears in the first two lines