Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Using File::Tail::select with STDIN?

by phillipdhall (Initiate)
on Aug 11, 2012 at 20:33 UTC ( #986921=perlquestion: print w/ replies, xml ) Need Help??
phillipdhall has asked for the wisdom of the Perl Monks concerning the following question:

First of all, a huge thank you to all the monks! I am on only my second Perl project, and y'all have made the journey successful. The answers available here have carried me through without ever having to ask my own question (until today)! That said...

My project tails a log (File::Tail) looking for a specific log entry. It then presents related data to the user. I want the user to be able to enter commands that alter the way the data is presented.

sub watch { my $tail = File::Tail->new( name=>"$filepath", interval=>1, maxinterval=>2, resetafter=>17 ); my @handles= ($tail, \*STDIN); while (1) { my (undef, undef, @pending) = File::Tail::select(undef, undef, undef, .5, @handles); foreach (@pending) { if ($_ == $tail) { my $line = $_->read(); <is it line I'm looking for... if so do blah...> } else { # assume STDIN <read user command, execute> } } } }

I can do this successfully using multiple files, but if I use \*STDIN, I get the following error when the watch() function is called:

Can't locate object method "predict" via package "FileHandle" at /mnt/home/phall/Scripts/lib/File/Tail.pm line 473.

Any guidance what the RIGHT way to do this is?

Thanks,
Phil

Comment on Using File::Tail::select with STDIN?
Download Code
Re: Using File::Tail::select with STDIN?
by Athanasius (Monsignor) on Aug 12, 2012 at 03:57 UTC

    Hello phillipdhall, and welcome to the Monastery!

    The error arises because File::Tail::select expects each element of your @handles array to be a File::Tail object. So, you would need to create a File::Tail object initialised with STDIN. Except you can’t, because the documentation for the constructor rules this out:

    name
     This is the name of the file to open. The file will be opened for reading. This must be a regular file, not a pipe or a terminal (i.e. it must be seekable).

    So, it seems you will need to find a different approach. Maybe something using a timeout? (See the example code for the select method.)

    I can’t experiment, as File::Tail won’t install on Windows. :-(

    Athanasius <°(((><contra mundum

      Per the File::Tail documentation:

      select is intended to enable the programmer to simoultaneously wait for input on normal filehandles and File::Tail filehandles. Of course, you may use it to simply read from more than one File::Tail filehandle at a time.

      Your answer makes sense because it doesn't seem to work, but I'd say that documentation is quite misleading. I did find this bug that may be related, but it hasn't been updated in 7 years, so I'm guessing development on this module stopped a long time ago, in a galaxy far, far away...

      So for now, I believe the fork/thread methods suggested by aitap and zentara are my next learning adventure...

Re: Using File::Tail::select with STDIN?
by aitap (Deacon) on Aug 12, 2012 at 09:52 UTC

    You can try opening and reading /dev/stdin or /proc/self/fd/0 on UNIX-like systems (at least on GNU/Linux).

    Another way is to use fork to process your file and the command line (see Term::ReadLine, Term::ReadKey etc.) in separate processes.

    Sorry if my advice was wrong.
Re: Using File::Tail::select with STDIN?
by zentara (Archbishop) on Aug 12, 2012 at 11:57 UTC
    You can also put your STDIN read into a thread. Here is an example where each key is detected, but you can write it more simply if you are willing to let STDIN to be line buffered, where the line comes in only after you hit a Return.
    #!/usr/bin/perl use warnings; use strict; use Term::ReadKey; use threads; $|++; # works non-blocking if read stdin is in a thread my $thr = threads->new(\&read_in)->detach; while(1){ print "test\n"; sleep 1; } sub read_in{ while(1){ my $char; if (defined ($char = ReadKey(0)) ) { print "\t\t$char->", ord($char),"\n"; #process key presses here if($char eq 'q'){exit} #if(length $char){exit} # panic button on any key :-) } } } __END__

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

      For anyone else searching, what I ended up doing was a little more simple. Looking into forking and threading was intimidating, since I'd need variables to be passed between the two and didn't understand how that worked.

      Since both standard select() and File::Tail::select have timeouts, I loaded STDIN and the File::Tail object in the appropriate one, and read from each inside a loop. Kinda silly how simple that seems to me now.

      I was initially concerned about performance when doing so, but then I guess all that each select method is doing is reading in a timed loop anyway. My method seems to be working great. Thank you to those who provided input.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://986921]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2014-08-30 05:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (291 votes), past polls