Re: Reopen file when contents changed?
by blazar (Canon) on Jun 07, 2005 at 13:39 UTC
|
You may simply want seek. I'm not really sure if it is well suited for that particular file, which may not be the case. It' worth trying in any case.
Incidentally under *NIX there's not strictly speaking anything like "text mode".
Update: I didn't want to test this myself, but in the end I did and it seems to work just fine:
#!/usr/bin/perl
use strict;
use warnings;
open my $fh, '<', '/proc/stat' or die $!;
{
print scalar <$fh>;
seek $fh, 0, 0;
sleep 1;
redo;
}
__END__
| [reply] [Watch: Dir/Any] [d/l] |
|
That code worked for me and generated output has follows:
cpu 757611 0 128970 6183389
cpu 757611 0 128970 6183507
cpu 757611 0 128970 6183608
cpu 757612 0 128970 6183708
cpu 757612 0 128970 6183809
| [reply] [Watch: Dir/Any] [d/l] |
Re: Reopen file when contents changed?
by Joost (Canon) on Jun 07, 2005 at 13:40 UTC
|
AFAIK you can just seek to the beginning and re-read, and the OS should take care that you get up-to-date data. (On *NIX systems)
| [reply] [Watch: Dir/Any] |
Re: Reopen file when contents changed?
by merlyn (Sage) on Jun 07, 2005 at 14:26 UTC
|
I'm not sure about things in /proc
Normally, if you stat the file and record dev/ino/ctime, and then read the data,
you can simply go into a sleep-1 loop until the stat of dev/ino/ctime changes.
For example:
my @base = stat($FILE);
my $base = "@base[0,1,10]"; # dev/ino/ctime
while (1) {
... read $FILE here, and process it ...
while (1) {
my @new = stat($FILE);
my $new = "@new[0,1,10]"; # current dev/ino/ctime
if ($base ne $new) { # changed
$base = $new; # reset
last; # restart outer loop
}
sleep 1; # delay because no change
}
}
| [reply] [Watch: Dir/Any] [d/l] |
Re: Reopen file when contents changed?
by bofh_of_oz (Hermit) on Jun 07, 2005 at 14:47 UTC
|
After reading all these *very* helpful advices, here's what i've got:
use strict;
use warnings;
while()
{
my @loads;
my $i = my $cpuload = 0;
open(INFIL,"< /proc/stat") || die("Unable To Open /proc/stat\n");
<INFIL> =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+).*/;
@loads = ($1, $2, $3);
sleep 1;
seek INFIL, 0, 0;
<INFIL> =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+).*/;
foreach ($1, $2, $3) { $cpuload += $_ - $loads[$i++]; }
close(INFIL);
print "$cpuload\n";
}
Tie::File looks interesting, so that might not be the final version ;)
Thanks a lot folks!
--------------------------------
An idea is not responsible for the people who believe in it...
| [reply] [Watch: Dir/Any] [d/l] |
|
use strict;
use warnings;
Good!
while()
I think that
while(1)
is more customary. To be fair I thought yours wouldn't have worked at all, but to be sure I tried and it did!
{
my @loads;
my $i = my $cpuload = 0;
open(INFIL,"< /proc/stat") || die("Unable To Open /proc/stat\n");
I, for one (but I'm not the only one!), recommend using the three args form of open. Also, low precedence or is better suited for flow control and you may benefit from including $! in the error message.
<INFIL> =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+).*/;
@loads = ($1, $2, $3);
This is overly complex for what it does, IMHO. I would probably do something like
my @loads = (<$fh> =~ /\d+/g)[0,1,2];
instead. Of course this is not strictly equivalent to yours. Indeed it may be worth to check that the line is the correct one. In that case you may still do something like this:
local $_=<$fh>;
(warn "something wrong!\n"), next
unless /^cpu\b/;
my @loads = (/\d+/g)[0..2];
sleep 1;
seek INFIL, 0, 0;
<INFIL> =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+).*/;
foreach ($1, $2, $3) { $cpuload += $_ - $loads[$i++]; }
close(INFIL);
So you want to iterate over the two lists in parallel. Now this is a situation in which some Perl6 features would turn out to be very handy!
However, and this is not strictly perl-specific, instead of getting the same info twice per cycle, you may get it once only, and take a "backup" version of it to compare with.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
$ perl -MO=Deparse -e 'while () {print "tick!\n"}'
while (1) {
print "tick!\n";
}
Update: Below is the deparse from perl 5.6.1. Above was 5.8.
$ perl561 -MO=Deparse -e 'while () {print "tick!\n"}'
for (;;) {
print "tick!\n";
}
Update 2: See more discussion in thread while ()
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Reopen file when contents changed?
by joelnackman (Beadle) on Jun 07, 2005 at 14:12 UTC
|
I'm not completely sure, but I think you should take a look at the Tie::File module. It allows you to treat a file like an array. Then you could just keep reading the first item in the array. Here's what it looks like:
use Tie::File;
...
tie @data, Tie::File, $filename or die "Can't tie to $filename :$!\n";
The @data array would then contain the contents of the file, one entry per line. You could then just read $data[0], which would be the first line of the file. I think that it would update dynamically.
Tie::File Documentation | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Reopen file when contents changed?
by salva (Canon) on Jun 07, 2005 at 13:59 UTC
|
just once per second? performance is not going to be an issue: use the aproach that seems simpler for you.
I would go for...
sub first_line_from_file {
my $fn = shift;
open my $file, "<", $fn
or die "unable to open file $fn";
scalar <$file>
}
update: added the scalar in front of <$file>. blazar, thanks for pointing it out. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
sub first_line_from_file {
my $fn = shift;
open my $file, "<", $fn
or die "unable to open file $fn";
<$file>
}
To return the first line I'd say
scalar <$file>;
(The semicolon is cosmetic. But I consider it good style) | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Reopen file when contents changed?
by Fletch (Bishop) on Jun 07, 2005 at 14:17 UTC
|
If you're on a system that supports it it might be more efficient to see if you could use SGI::FAM, or look into wrapping kqueue on a BSD box using Inline::C. That'd let the OS notify you just when it changes.
Update: Oh, just read that you're reading stuff from /proc; never mind as my suggestions probably won't work there.
--
We're looking for people in ATL
| [reply] [Watch: Dir/Any] |
|
Nothing much to do with the original question ... I hadn't found about about kqueue before until I browsed this node - looks very useful.
Thanks!
| [reply] [Watch: Dir/Any] |
Re: Reopen file when contents changed?
by Thargor (Scribe) on Jun 07, 2005 at 16:10 UTC
|
I am not sure if it would work but couldn't you just create a pointer to the first line of the file when you initially read it in and just leave it static? | [reply] [Watch: Dir/Any] |
|
Huh? Pointer? static? What are you talking about?
| [reply] [Watch: Dir/Any] |
|
When you open a file you have a pointer that points at the first line of the file. So I was just suggesting the OP might want to try just dupelicating that pointer and not changing what it points to while the original file pointer loops through the rest of the lines of the file reading in the data. I am slightly new to perl not sure if it would work. Hence it would be a static pointer, one that does not change, which is all the OP needs a constant pointer to the first line of the file. This would only be usefull if he isn't adding any information to the top of the file because the pointer would then not be pointing to the correct line.
| [reply] [Watch: Dir/Any] |
|
Re: Reopen file when contents changed?
by thcsoft (Monk) on Jun 07, 2005 at 14:09 UTC
|
| [reply] [Watch: Dir/Any] |
|
Possibly, but AFAIK forking a shell requires resources, which will impact the measurements; opening a file will not...
--------------------------------
An idea is not responsible for the people who believe in it...
| [reply] [Watch: Dir/Any] |
|
Forking anything would require resources... There is no need to fork just to read the contents of a file.
But anyway, I think keeping the file open and just seek(0) is easier than opening the file everytime. I don't know how much impact open() would generate on load, but I do think seek(0) would cause even less impact.
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] |