From owner-freebsd-questions@FreeBSD.ORG Mon Dec 22 08:28:00 2008 Return-Path: Delivered-To: freebsd-questions@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B0C1B1065676 for ; Mon, 22 Dec 2008 08:28:00 +0000 (UTC) (envelope-from jonathan+freebsd-questions@hst.org.za) Received: from hermes.hst.org.za (onix.hst.org.za [209.203.2.133]) by mx1.freebsd.org (Postfix) with ESMTP id CD6788FC14 for ; Mon, 22 Dec 2008 08:27:58 +0000 (UTC) (envelope-from jonathan+freebsd-questions@hst.org.za) Received: from sysadmin.hst.org.za (sysadmin.int.dbn.hst.org.za [10.1.1.20]) (authenticated bits=0) by hermes.hst.org.za (8.13.8/8.13.8) with ESMTP id mBM8F11A055623 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Mon, 22 Dec 2008 10:15:02 +0200 (SAST) (envelope-from jonathan+freebsd-questions@hst.org.za) From: Jonathan McKeown Organization: Health Systems Trust To: freebsd-questions@freebsd.org Date: Mon, 22 Dec 2008 10:28:37 +0200 User-Agent: KMail/1.9.7 References: <20081221053407.GA87868@thought.org> <20081221140658.GA24691@marge.bs.l> <20081221222744.GA28185@thought.org> In-Reply-To: <20081221222744.GA28185@thought.org> X-Face: $@VrUx^RHy/}yu]jKf/<4T%/d|F+$j-Ol2"2J$q+%OK1]&/G_S9(=?utf-8?q?HkaQ*=60!=3FYOK=3FY!=27M=60C=0A=09aP=5C9nVPF8Q=7DCilHH8l=3B=7E!4?= =?utf-8?q?2HK6=273lg4J=7Daz?=@1Dqqh:J]M^"YPn*2IWrZON$1+G?oX3@ =?utf-8?q?k=230=0A=0954XDRg=3DYn=5FF-etwot4U=24b?=dTS{i X-Spam-Score: -4.077 () ALL_TRUSTED,AWL,BAYES_00 X-Scanned-By: MIMEDefang 2.61 on 209.203.2.133 Subject: Re: Sed question X-BeenThere: freebsd-questions@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: User questions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 22 Dec 2008 08:28:00 -0000 On Monday 22 December 2008 00:27:44 Gary Kline wrote: > > anyway, this is one for giiorgos, or another perl wiz. i've > been using the perl subsitution cmd one-liner for years with > unfailing success. is there a way of deleting lines with perl > using the same idea as: > > perl -pi.bak -e 's/OLDSTRING/NEWSTRING/g' file1 file2 fileN For a single file it's very easy: perl -ne 'print unless 8..10' filename will print every line except lines 8, 9 and 10. The .. or range operator (in scalar context) is a sort of flip-flop. It keeps its own state, which is either true or false. When it's false it only evaluates its left-hand argument; when it's true it only evaluates its right-hand argument; and whenever the argument it's currently looking at returns true, the expression changes state. If the argument is an integer, it's treated as a comparison against the current line number, $. ; so the first expression, 8..10, means ($. == 8) .. ($. == 10) It's false to start with, until ($. == 8) returns true (on line 8); it becomes true and remains true until ($. == 10) returns true (on line 10), when it becomes false again and remains false until it next sees line number 8. You can also use more complicated tests in the range operator: perl -ne 'print unless /START/ .. /END/' will find each line containing the word START anywhere, and delete from that line to the next line containing END (inclusive of both endpoints) - this will work for multiple occurrences of START and END in your file. There are two problems if you string multiple files together on the command line: first, if you're using line numbers, the line number doesn't reset between files unless you do an explicit close on each file. The bigger problem is if you have a file in which the second condition doesn't occur (a file with only 9 lines in the first example, or a file with a START and no corresponding END in the second case): the range operator will stay true until it sees the ending condition in the next file, meaning you'll lose the first ten lines in the numeric case, or every line from the top of file to the first END in the second case. To get round these two problems, you need to test for eof in the range operator, and close each file when it hits eof to reset the line count. perl -ne 'print unless 8 .. $. == 10 || eof; close ARGV if eof' file[1-n] perl -ne 'print unless /START/../END/ || eof; close ARGV if eof' file[1-n] There's some hairy precedence in the first range expression: a useful tip for checking that you've got it right (and indeed in general for checking that a bit of Perl does what you think it does) is the B::Deparse core module, which you call like this: perl -MO=Deparse,-p -e 'print unless 8 .. $. == 10 || eof' which outputs ((8 .. (($. == 10) || eof)) or print($_)); -e syntax OK The ,-p argument to -MO=Deparse tells it to put in parentheses everywhere. If you're like me and like to leave them all out, feed your expression to Deparse with all the parens in and leave off the ,-p argument: Deparse will get rid of all the unnecessary ones: $ perl -MO=Deparse -e 'print unless (8 .. (($. == 10) or eof))' print $_ unless 8 .. $. == 10 || eof; -e syntax OK Jonathan