LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-16-2009, 06:40 PM   #1
zugvogel
LQ Newbie
 
Registered: Sep 2005
Location: Tokyo, Japan
Distribution: Mac, Ubuntu, Debian and Centos
Posts: 28

Rep: Reputation: 16
sed: delete lines after last occurrence of a pattern in a file


Hello,

I'm having trouble finding how to delete all lines after the last occurrence of a pattern.

I know this deletes all lines after it finds PATTERN:
sed '/PATTERN/q' file.in > file.out

but if I have a file like:
qwe
PATTERN
rty
PATTERN
uiop

the result is:
qwe
PATTERN

when in fact I want:
qwe
PATTERN
rty
PATTERN

Can anyone tell me how I achieve this? I found lots of references with google to replacing the last occurrence of one word with another within a line, but not something like the case I have.

Thanks!
 
Old 11-16-2009, 08:05 PM   #2
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
I'm not entirely sure that sed is the best tool for this job. The problem is that sed is so line-oriented, and you need to maintain state (at least at some level) to do this.

A quick stab at how I might do this (probably in Perl): track through the file line by line; every time I see PATTERN, note the line number (just one variable for this $last_seen); then rewind to the top of the file and only print back out up to the line number that I was left with.

The general problem is that whatever tool you use has no obvious way of knowing whether the next line is another occurrence of PATTERN or EOF.

An example, not very fancy:
Code:
#!/usr/bin/env perl
use strict;
use warnings;

open my $fh, '<', 'file.txt'
	or die "Can't open 'file.txt' for reading: $!";

my $last_seen;

while (<$fh>) {
	$last_seen = $. if $_ =~ /PATTERN/;
	print $., "\n";
}

open my $out, '>', 'new_file.txt'
	or die "Can't open 'new_file.txt' for writing: $!";

seek($fh, 0, 0);
$. = 0;

while (<$fh>) {
	print $out $_;
	print $., "\n";
	last if $. == $last_seen;
}

Last edited by Telemachos; 11-16-2009 at 08:22 PM.
 
Old 11-16-2009, 10:18 PM   #3
zugvogel
LQ Newbie
 
Registered: Sep 2005
Location: Tokyo, Japan
Distribution: Mac, Ubuntu, Debian and Centos
Posts: 28

Original Poster
Rep: Reputation: 16
Thanks!

Hi Telemachos,

I suppose the difficulty of doing this with sed is why I was unable to find an appropriate sed-based solution with google.

Thank you for your kind help and demonstrating a perl-based solution. In the end, taking note about what you said about "rewind", I have written a fortran program to selectively read in data, using the "backspace" command to go back through the file when needed.

Thank you again!
 
Old 11-16-2009, 11:16 PM   #4
ghostdog74
Senior Member
 
Registered: Aug 2006
Posts: 2,697
Blog Entries: 5

Rep: Reputation: 244Reputation: 244Reputation: 244
@OP,you should learn how to use gawk instead.
Code:
$ more file
qwe
PATTERN
rty
PATTERN
uiop
blah blah PATTERN
lksf
lasd
PATTERN
end

$ gawk -vRS="PATTERN" 'NR>1{print s RT} {s=$0}' ORS=""  file
qwe
PATTERN
rty
PATTERN
uiop
blah blah PATTERN
lksf
lasd

$ more file
qwe
PATTERN
rty
PATTERN
uiop
blah blah PATTERN
lksf
lasd
end

$ gawk -vRS="PATTERN" 'NR>1{print s RT} {s=$0}' ORS=""  file
qwe
PATTERN
rty
PATTERN
uiop
blah blah
 
Old 11-17-2009, 01:49 AM   #5
Kenhelm
Member
 
Registered: Mar 2008
Location: N. W. England
Distribution: Mandriva
Posts: 360

Rep: Reputation: 170Reputation: 170
tac can simplify the problem for sed by temporally reversing the order of the lines.
The '0' address is a GNU sed extension and is needed here in case PATTERN is on line 1 of the reversed file.
Code:
echo 'qwe
PATTERN
rty
PATTERN
uiop' | tac | sed  '0,/PATTERN/{/PATTERN/!d}' | tac

qwe
PATTERN
rty
PATTERN
This uses a loop ':a N;$!ba' to put all the lines into the sed pattern space then a 's' command to delete anything after the last PATTERN.
Code:
echo 'qwe
PATTERN
rty
PATTERN
uiop' | sed ':a N;$!ba; s/\(.*PATTERN\).*/\1/'

qwe
PATTERN
rty
PATTERN
 
  


Reply

Tags
sed



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
sed delete lines from file one if regexp are listed in file two fucinheira Programming 6 09-17-2009 08:28 AM
[SOLVED] SED and Replacing Specific occurrence or Range of Lines bridrod Linux - Newbie 7 08-27-2009 09:59 AM
[sed || gawk]: find and delete blocks and lines from file Hisu Programming 1 09-16-2008 02:01 PM
Insert and delete lines at the end of a file using sed DriveMeCrazy Programming 1 01-05-2007 01:45 AM
awk/gawk/sed - read lines from file1, comment out or delete matching lines in file2 rascal84 Linux - General 1 05-24-2006 09:19 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:26 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration