Re: Simulating UNIX's "tail" in core Perl
by archon (Monk) on Mar 07, 2001 at 03:00 UTC
|
Have you looked at File::Tail? Just because there's a module that does what you want, doesn't mean you have to use it. You can look through it for hints and, depending on the license, use its code in your own program. | [reply] [d/l] |
Re: Simulating UNIX's "tail" in core Perl
by mr.nick (Chaplain) on Mar 07, 2001 at 07:15 UTC
|
Almost directly from "perldoc -f seek"
#!/usr/bin/perl
open FILE,"<foo.txt" || die $!;
for (;;) {
for ($curpos = tell(FILE); $_ = <FILE>;
$curpos = tell(FILE)) {
## do something exciting here
print;
}
sleep(1);
seek(FILE, $curpos, 0);
}
| [reply] [d/l] |
|
|
Please pardon my extreme newbie-ness on this one. I read over the seek stuff and found that example yesterday, but I can't figure out how it works. Can someone explain (in very small words so I'll be sure to understand) how this will let me grab lines 1027 to 1031 from a 2500+ line file? Thanks. (Again, my appologies if I'm being a complete dunce.)
-Gryphon.
| [reply] [d/l] |
|
|
It won't.
There are no shortcuts for finding the 1027th line
of a text file containing arbitrary lines of arbitrary
length. Now, if you want the last 100 lines, then seek
can be a very effective short cut.
If the lines are of fixed length or you can identify the
desired lines by something other than sequence
(for example, if the text of the lines contain line
numbers), then you can probably come up with shortcuts
that don't involve just reading all of the lines and
looking for and counting the newlines.
-
tye
(but my friends call me "Tye")
| [reply] |
(RhetTbull) Re: Simulating UNIX's "tail" in core Perl
by RhetTbull (Curate) on Mar 07, 2001 at 04:58 UTC
|
You asked for perl, but if you have sed, you could just do:
sed -e :a -e '$q;N;11,$D;ba' filename
That will emulate "tail"
To print out a range:
sed -n -e '1034:1047p' filename
or, you could print out a couple of ranges:
sed -n -e '1034:1047p' -e '2034:2047p' filename
Oh, you wanted PERL...! :-) OK,
perl -e 'print `sed -n -e '1034,1047p' filename`'
Of course, if you really need a "perl only" solution, one of the other replies here would be better :-) But perl's often not the only tool in the box!
| [reply] [d/l] [select] |
Re: Simulating UNIX's "tail" in core Perl
by RhetTbull (Curate) on Mar 07, 2001 at 05:21 UTC
|
You might also take a look at the Perl Power Tools (aka Unix Reconstruction Project). They've got quite a few unix utilities written in pure perl. Their "tail" implementation is rather complete -- ie. it emulates all tail behavior including command line options, etc. but will give you a good starting point.
--RT | [reply] |
Re: Simulating UNIX's "tail" in core Perl
by petral (Curate) on Mar 07, 2001 at 03:30 UTC
|
perl -wn000e '$start=1034; $end=1047; $pre=$start-1; $len=$end-$start+
+1;
print / (?:.*\n){$pre} ( (?:.*\n){$len} ) /mx
' <<filename>>
p | [reply] [d/l] |
|
|
right, it's 10am here in London so i'm not really with it yet. Would someone care to explain this post to me ?
tnx
Jorg
| [reply] |
|
|
"Give me lines 1034 through 1047 of the file."
perl -wn000e # -n read file, 000 set $/ (input separator) to n
+il
# hence, read whole file into $_
'$start = 1034; # number of the line to start printing
+at
$end = 1047; # number of the line to end printing at
$pre = $start - 1; # the number of lines to skip before pr
+inting
$len = $end - $start + 1; # the number of lines to print
print / (?: # don't remember this () in $1..$n
.*\n # all chars up to a newline and the \n
){$pre} # pre number of times
( # remember this (return it as first el of
+list)
(?: # but don't remember this part as 2nd elem
+ent
.*\n # match a line up to and including \n
){$len} # number-of-lines-to-print times
) # return as single (multi-line) value
/mx' # /m: don't let '.' match \n (may not be n
+ecessary)
p | [reply] [d/l] |
Re: Simulating UNIX's "tail" in core Perl
by EvanK (Chaplain) on Mar 07, 2001 at 03:52 UTC
|
Well, if you don't wanna use outside modules, you probably won't wanna use outside programs, but in case you do, there's a tail-type program at Virtually Un*x
______________________________________________
When I get a little money, I buy books. If I have any left over, I buy food and clothes.
-Erasmus
| [reply] [d/l] |
Re: Simulating UNIX's "tail" in core Perl
by hurstdog (Initiate) on Mar 07, 2001 at 05:38 UTC
|
well, if you're not too worried about file size, just read the whole file into an array, and pop() off as many lines as you need. Course, that won't be the most optimal solution on large files. i.e.
my @file = <FILE>;
for( my $i=0; $i<$MAXLINES; $i++ ) {
print pop @file;
}
| [reply] [d/l] |
|
|
hmm... guess I shoulda read you're original post more clearly, mostly the part about how the file will be "really large" ;-) oh well...
| [reply] |
Re: part two
by smoo (Initiate) on Mar 08, 2001 at 19:52 UTC
|
At the risk of stating the obvious, for your 'lines 1034-1047' problem I can see two efficient solutions.
1: If you have control of the source file make it fixed-width (possibly not even newline terminated records), then seeking a particular 'line' (record) is a simple bit of math and a seek().
2: If the file must have variable length lines, and assuming you do the mid-file extraction frequently and that the file persists for a relatively long time...
Scan the file first for newlines.
Record the position of each newline in a second index file.
To look at a particular line:
Open up the index file.
Scan to the line# you're looking for.
seek() on the file you want to look at.
If the requested lines are past the end of the index:
seek() to the last known newline in the source file
index to the end (appending to your index file).
Another optimization is to store the index in binary format (ie: integers) so that you can find the index for line #27231 simply by seeking to (size_of_rec * 27231).
The algorithm in 2 can be implemented in Perl, but would be simple to do in C or C++ which would make it run pretty fast too.
Steve-
| [reply] |
Re: Simulating UNIX's "tail" in core Perl
by fpi (Monk) on Mar 07, 2001 at 14:18 UTC
|
Well, the simplest way to tail a file is to actually just use tail. But I guess this would only work if you are on a Unix platform. You didn't mention what platform you were on, but for those who might find this useful:
my @lines=`tail logfile.txt`;
One line - can't get much simpler than that. By the way, those are back quotes, not single quotes. If you've never used them before, backquotes will execute whatever is within them like it is at the command prompt, then will return the resulting lines as an array. And whatever command you send has to exist on your system. I really don't know what backquotes do on Mac or Win.
| [reply] [d/l] |
|
|
my thoughts exactly. I had a similar situation and just used tail within backquotes. you can control the lines outputted with tail just as you would on the command line.
| [reply] |
Re: Simulating UNIX's "tail" in core Perl
by Anonymous Monk on Mar 08, 2001 at 03:40 UTC
|
| [reply] |
|
|
POE::Wheel::FollowTail is great because you can deal with it in callbacks.
File::Tail has other features, like the file doesn't have to already be there and you can move the file out from under it and slip in a new one and it keeps on ticking, etc.
p
| [reply] |
Re: Simulating UNIX's "tail" in core Perl
by MadraghRua (Vicar) on Mar 08, 2001 at 03:12 UTC
|
For Part 1 try the following snippet which calls the unix tail function:
system("tail -10 inputFile.txt >> outputFile.txt");
open <FILE, "</path/to/outputFile.txt"> or die "can't open file: $!\n"
+;
#do whatever here
close FILE or die "can't close file $!\n";
In general, I do rely on calling Unix command line utilities for tools that I don't want to reinvent. I don't work on PC so much, so I'm not familiar with the situation there.
A 'pure' Perl implementation might involve counting all the lines in a file and then using the count to generate a new file which you would then process. This is more a Part 2ish approach. So for Part 2, try something like the following:
@input = <FILE>;
$i = 0;
foreach ($line (@input)) {
$i++;
if ($i >= 1034 || $i <= 1047) {
#process here
}
}
close FILE or die "can't close file: $!\n";
To count all the lines, just count your way through the foreach loop and use the final $i value as the number of lines in your file. This should help, I hope.
Have a look at chapters 8 and 9 of the Perl Cookbook - I just noticed a simple and elegant way of doing the line counting there. You might also try working with this PerlMonks node:
Parsing a file one line at a time. I found this one to be quite helpful for this type of problem in general.
Good luck!
MadraghRua yet another biologist hacking perl.... | [reply] [d/l] [select] |
Re: Simulating UNIX's "tail" in core Perl
by Anonymous Monk on Mar 08, 2001 at 07:23 UTC
|
I use the module ::ReadBackwards to do something similar to what you are trying. | [reply] |
Re: Simulating UNIX's "tail" in core Perl
by lzcd (Pilgrim) on Mar 08, 2001 at 07:54 UTC
|
My personal pick (irrespective of language) for an 'inside' solution would be to create a FILO (First In Last Out) style of collection to the same size as the number of lines you wish to 'tail'.
You then can go through the entire file, reading in a line to the collection and removing the oldest line from the collection.
To paraphrase one example:
Create an Array equal in size to number of 'tail elements' required
Set WritePointer to the First Element
Start Loop
Read Line into Array at WritePointer
Increment WritePointer
If WritePointer>Number of Array Elements then Set WritePointer to First Array Element
End Loop if end of File
At the end of this you'll have an array containing the required lines. All you've got to do is read the array elements back in the 'reverse direction' until you get back to the WritePointer element.
If all of this seems like a load of old horse dribbblings to you, I'd suggest looking up the term FILO in your nearest Computer Science book. :)
| [reply] |