Shakiba Moshiri
Shakiba Moshiri

Reputation: 23794

reading a key from keyboard while the process is running

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.

Upvotes: 3

Views: 2288

Answers (3)

Prashant Pokhriyal
Prashant Pokhriyal

Reputation: 3827

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";
    }
}

References:

Upvotes: 0

Borodin
Borodin

Reputation: 126722

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;
}

Upvotes: 2

carlosn
carlosn

Reputation: 433

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.

Upvotes: -1

Related Questions