Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I wrote a simple script to manage the download time ( start and finish ) with wget in Linux-Gnu with Perl. There is no problem and everything works good, except I wish I could read a key from keyboard when the process is running.
I show a simple movement animation on the screen that I do not want to stop it and then read the key.

for example like mplayer or mpv that when you run it on the comman-line, you can press q to exit or s to take a picture from the screen.

A part of the script:

do {
    system( "clear" );
    ($h, $m, $s) = k5mt::second_to_clock(  $till_shutdown );

    set_screen();
    set_screen( 24 );

    set_screen();
    say "download was started ...";

    set_screen();
    set_screen( 24 );

    set_screen();
    printf "till finish:    %02d %02d %02d\n", $h, $m, $s;

    set_screen();
    set_screen( 24 );

    set_screen();
    say "wget pid: [$wget_pid]";

    set_screen();
    set_screen( 24 );

    set_screen();
    $waiting_temp .= $animation[ $counter_animation++ ];
    say $waiting_temp;
    if( length( $waiting ) == $counter_animation ){
        $waiting_temp = "";
        $counter_animation = 0;
    }

    set_screen();
    set_screen( 24 );

    sleep 1;
    $till_shutdown--;

} while( $till_shutdown );  

enter image description here

the words waiting till finish..... is shown consecutively ( without interruption ) and I want to read a key like q to exit from the program.

UPDATE

I am looking for a solution with as many option as I want, if I have wanted just for exit from the program I simply tell the user to press Ctrl + C

Is it possible with scripting in Perl? or not? If it is, How?

NOTE: if it is possible without any modules please say the solution without any module, and if not, okay no problem

However, thank you so much.

share|improve this question
    
One solution that I see is creating a sub process and do the main task while parent will keep looking for a input if user enters q then parent will kill the sub proccess – Prashant Pokhriyal Jan 20 at 15:40
    
@PrashantPokhriyal Your meaning is I use fork() for the reading key ? – k-five Jan 20 at 15:42
    
Yes Create a fork – Prashant Pokhriyal Jan 20 at 15:43
    
@PrashantPokhriyal It is worth to examine, thank :) – k-five Jan 20 at 15:45
    
Have you tried to use Term::ReadLine ? doc – carlosn Jan 20 at 16:00
up vote -1 down vote accepted

This will work for you

use 5.010;
use strict;
use Term::ReadKey;

ReadMode 4; # It will turn off controls keys (eg. Ctrl+c)

my $key; 

# It will create a child so from here two processes will run.
# So for parent process fork() will return child process id
# And for child process fork() will return 0 id

my $pid = fork(); 

# This if block will execute by the child process and not by 
# parent because for child $pid is 0     

if(not $pid){

   while(1){

        # Do your main task here
        say "hi I'm sub process and contains the main task";
        sleep 2;
    }
}

# Parent will skip if block and will follow the following code
while (1) {

   $key = ReadKey(-1); # ReadKey(-1) will perform a non-blocked read

   if($key eq 'q'){    # if key pressed is 'q'

      `kill -9 $pid`;   # then if will run shell command kill and kill
                        # the child process
       ReadMode 0;      # Restore original settings for tty.

       exit;            # Finally exit from script

    } elsif( $key eq 'h' ) {

         say "Hey! you pressed $key key for help";
    } elsif( $key ne '' ) {

        say "Hey! You pressed $key";
    }
}

For Term::ReadKey Documentation---->Term::ReadKey

For better understanding of forking in perl -----> Fork in Perl

share|improve this answer
    
Please explain how it works. – k-five Jan 20 at 17:36
    
@k-five I edited my answer. Hope it will help you – Prashant Pokhriyal Jan 20 at 17:55
    
I have doubt that it can manage more than one key or not. And I see it cannot mange more than one key in the second while(1), Am I right or not? – k-five Jan 20 at 17:58
    
Yes you are right I gave only one option i.e, 'q'. You can provide as many option as you want in the if block – Prashant Pokhriyal Jan 20 at 17:59
    
It works but It cannot handle more than one key. I added h key for show the help, and after that no keys works, thanks anyway – k-five Jan 20 at 18:15

Assuming your program has a central loop, or if you can simply fit keyboard checks into the processing, you are better off using Term::ReadKey than trying to fit in fork and handling the necessary inter-process communication

Calling ReadKey(-1) will do a non-blocking read, which will return a single character if a key has been hit, or undef otherwise. (You may supply a second parameter which is the IO channel to be used, but it will default to STDIN.)

I suggest you run this example. I have used sleep 1 as a dummy for your loop processing

use strict;
use warnings 'all';
use feature 'say';

use Term::ReadKey;

my $n;

while () {

    my $key = ReadKey(-1);

    say "key $key entered" if defined $key;

    sleep 1;

    say ++$n;
}
share|improve this answer

If you are not worried about a little delay, you can play with select and alarm to try to handle user input.

Using select you can handle the STDIN and using alarm you can wait for a few seconds or less for user input.

#!/usr/bin/env perl 

use common::sense;

use IO::Select;

my $sel = IO::Select->new();
$sel->add( \*STDIN );

$|++;

my $running = 1;

while ($running) {

    eval {

        local $SIG{ALRM} = sub { die 'Time Out'; };

        alarm 0.5;

        if ( $sel->can_read(0) ) {

            my $input = <STDIN>;

            chomp $input;

            print "STDIN > $input\n";

            $running = 0;

        }
    };

    if ( !$@ ) {

        select( undef, undef, undef, 0.5 );

        print ".";

    }
}

print "\nEnd Of Script\n";

I told you to try to use Term::ReadKey :-)

Here we go:

!/usr/bin/env perl 

use common::sense;
use Term::ReadKey;

my $running = 1;
my $key;

while ($running) {
    ReadMode 4;    # Turn off controls keys
    while ( not defined( $key = ReadKey(-1) ) ) {

        select( undef, undef, undef, 0.5 );

        print '.';

    }
    print "\nSTDIN> $key\n";
    ReadMode 0;  

}

print "\nEnd Of Script\n";

Now you just have to handle signals like quit or int to exit the loop, but using this you can capture the input and do whatever you want.

share|improve this answer
    
Of course I am WORRIED, this is not a good solution, thanks though for attempting – k-five Jan 20 at 17:12
    
@k-five I just added something that might works – carlosn Jan 20 at 17:51
    
here is your comment: Have you tried to use Term::ReadLine you told to use Readline – k-five Jan 20 at 18:00
    
@k-five Almost there.... Almost there ! – carlosn Jan 20 at 18:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.