Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Checing presence of input on standard input in non-blocking way, sans user interaction

by Anonymous Monk
on Jul 17, 2013 at 13:31 UTC ( [id://1044806]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Is there a way to check if input is present on standard input (*STDIN) ...

  • in a non-blocking way
  • without waiting for user interaction
  • in any version of perl 5 with bulit-in functionality (other than via &Term::ReadKey::ReadKey)
... when input is provided as program.pl < file?

I had tried (before finding T'R) ...

  • both of $in = <STDIN> and getc( STDIN ) wait for user input;
  • -[tpS] file tests don't provide the information.

  • Comment on Checing presence of input on standard input in non-blocking way, sans user interaction
  • Select or Download Code

Replies are listed 'Best First'.
Re: Checing presence of input on standard input in non-blocking way, sans user interaction
by marinersk (Priest) on Jul 17, 2013 at 15:35 UTC
    If I read you correctly, you are looking for a Perl version of the BASIC $Inkey function.

    I think in C it was usually the keyPress() function.

    In other words, you want to be able to see if there's a reason to process input without stopping your program to wait for it. AND you want it to work even if the user had redirected input.

    There is no pure Perl solution to this that I'm aware of -- over the years, I have checked from time to time.

    Not sure why you wish to avoid using Term::Readkey but without that I would expect you to not find a solution.

    If you do find one, I'd personally appreciate knowing about it here on Perlmonks.

    Good luck in the hunt!

      "In other words, you want to be able to see if there's a reason to process input without stopping your program to wait for it. AND you want it to work even if the user had redirected input." -- marinersk

      Indeed. My objective is to collect input from any & all of the ways, as in command | program.pl file < file2.

      "Not sure why you wish to avoid using Term::Readkey but without that I would expect you to not find a solution." -- marinersk

      It is not that I want to avoid use of T::R. I am|was curious if I might have missed something otherwise obvious to others (say on the lines of readline, sysread, select, tell, etc).

        Ah, understood.

        I will be watching this thread for awhile; if there is a procedure (or even a trick) out there for capturing this, I'd be interested in knowing it.

        Not holding my breath, mind you, but definitely interested.

        :-)

Re: Checing presence of input on standard input in non-blocking way, sans user interaction
by Preceptor (Deacon) on Jul 17, 2013 at 20:57 UTC

    There's two ways I can think of to do it - one is via threading your perl, to make your 'reading STDIN' run in parallel. Something like:

    #!/usr/bin/perl use strict; use warnings; use threads; use Thread::Queue; my $input_q = Thread::Queue -> new(); sub stdin_reader { while ( my $line = <STDIN> ) { if ( defined $input_q -> pending() ) { $input_q -> enqueue ( $line ); } else { last; } } } sub doing_stuff_thread { my $count = 0; while ( $count < 10 ) { my $item = $input_q -> dequeue_nb(); if ( defined $item ) { print "Got: $item"; $count++; } else { print "Doing something else, because nothing on STDIN\n"; print "Pending items:", $input_q -> pending(), "\n"; sleep 1; } } $input_q -> end(); } threads -> create ( \&stdin_reader ); threads -> create ( \&doing_stuff_thread ); foreach my $thr ( threads -> list() ) { print "Waiting for ",$thr -> tid()," to join\n"; $thr -> join(); }

    That works, although bear in mind that 'STDIN' is being read line by line, so you need to put carriage returns in. (you can't do 'readkey' style). (And also - you may find 'Thread::Queue' isn't 'standard', but I'm sure you can figure out how to do something useful with thread::shared and lock instead.

    The other way I can think of involves using IO::Select. Or perhaps like this node: Here

    #!/usr/bin/perl use strict; use warnings; use IO::Handle; use IO::Select; my $nb_stdin = new IO::Select( *STDIN ); while ( 1 ) { if ( $nb_stdin -> can_read(0) ) { my $line = <STDIN>; print "Got: ", $line,"\n"; } else { print "Nothing on STDIN, doing something else\n"; sleep 1; } }

    This doesn't seem to work properly on Win32/Activestate though - I believe I've seen something to the effect that IO::Select doesn't work on non-sockets on W32. YMMV

      This doesn't seem to work properly on Win32/Activestate though - I believe I've seen something to the effect that IO::Select doesn't work on non-sockets on W32

      Actually, the root of the problem is the select implementation available on Windows that only supports TCP sockets.

Re: Checing presence of input on standard input in non-blocking way, sans user interaction
by Anonymous Monk on Jul 17, 2013 at 13:34 UTC
    Also, could somebody please correct the "Checking" in title?
Re: Checing presence of input on standard input in non-blocking way, sans user interaction
by wet (Initiate) on May 25, 2015 at 06:05 UTC
    I have posted this here because I had in the past implemented<>this using different approaches and none where to my satisfaction (made by me :-), I know this is a rather old post, yet this problem is rather difficult one otherwise and the code is almost self explanatory. It is based on sysread almost word for word from the perl cookbook or programming perl, the blocking factor is set to 2 bytes, in the event I want port this to win 32 'CRLF'.
    sub getcmd { my $cin=''; my $buf=''; my $len=0; my @cin=(); local $/=undef; while ($RUN) { print(STDERR ' '); $buf=''; $len=0; while ($len=sysread(STDIN,$buf,2)) { if (! defined($len)) { next if ($! =~ /^Interrupted/); fatal(0xff,"system read fault",$!); } $cin.=$buf; last if (rindex($buf,"\n",0) > -1); } chomp($cin); if ($cin) { @cin=split(/\s+/,$cin); print(STDERR "CIN: $cin\n") if $VRB; last; } else { $RUN--; unless ($RUN) { print(STDERR "EXIT: timeout no activity\n") if $VRB; print(0x11); } sleep 1 } } return(@cin); }
      in my haste - forgot to add, you need to do this before the call to getcmd(). this is from the standard Fcntl package and the sets the file handle to non blocking by adding to the flags, you can reset the flag afterwards.
      
      fcntl(STDIN, F_GETFL, $flg) or die("Couldn't get flags for HANDLE : $!\n"); fcntl(STDIN, F_SETFL, ($flg | O_NONBLOCK)) or die("Couldn't set flags for HANDLE: $!\n");
Re: Checing presence of input on standard input in non-blocking way, sans user interaction
by Anonymous Monk on Jul 17, 2013 at 14:18 UTC

    use the shift function to get standard input provided like that.

      I am unaware of how shift can be used to indicate whether the user has typed something on the keyboard.

      I am also unaware of how shift can be used to determine if there is any data waiting to be read on STDIN from redirected input.

      Could you be so kind as to provide a working code example? That would be really cool information to have!

      It's not standard input by the way, you have rerouted standard output in the other program.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2024-04-19 09:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found