Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I read a single character from STDIN using Perl on Windows?

Tags:

windows

perl

Using Perl, how do I capture a single character from STDIN without needing the user to hit enter (similar to C's getch() function)?

Perl has a getc() function, but according to the perlfunc:

However, it cannot be used by itself to fetch single characters without waiting for the user to hit enter.

The perlfunc docs do provides a way to read a single character using getc() but it requires manipulating the terminal settings using stty. The script I'm writing needs to work on Windows (without cygwin, msys, etc.) - so that's not an option.

like image 1000
Mike Willekes Avatar asked Jan 07 '10 15:01

Mike Willekes


2 Answers

From perlfaq5's answer to How can I read a single character from a file? From the keyboard?


You can use the builtin getc() function for most filehandles, but it won't (easily) work on a terminal device. For STDIN, either use the Term::ReadKey module from CPAN or use the sample code in getc in perlfunc.

If your system supports the portable operating system programming interface (POSIX), you can use the following code, which you'll note turns off echo processing as well.

#!/usr/bin/perl -w
use strict;
$| = 1;
for (1..4) {
    my $got;
    print "gimme: ";
    $got = getone();
    print "--> $got\n";
    }
exit;

BEGIN {
use POSIX qw(:termios_h);

my ($term, $oterm, $echo, $noecho, $fd_stdin);

$fd_stdin = fileno(STDIN);

$term     = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm     = $term->getlflag();

$echo     = ECHO | ECHOK | ICANON;
$noecho   = $oterm & ~$echo;

sub cbreak {
    $term->setlflag($noecho);
    $term->setcc(VTIME, 1);
    $term->setattr($fd_stdin, TCSANOW);
    }

sub cooked {
    $term->setlflag($oterm);
    $term->setcc(VTIME, 0);
    $term->setattr($fd_stdin, TCSANOW);
    }

sub getone {
    my $key = '';
    cbreak();
    sysread(STDIN, $key, 1);
    cooked();
    return $key;
    }

}

END { cooked() }

The Term::ReadKey module from CPAN may be easier to use. Recent versions include also support for non-portable systems as well.

use Term::ReadKey;
open(TTY, "</dev/tty");
print "Gimme a char: ";
ReadMode "raw";
$key = ReadKey 0, *TTY;
ReadMode "normal";
printf "\nYou said %s, char number %03d\n",
    $key, ord $key;
like image 111
brian d foy Avatar answered Sep 21 '22 10:09

brian d foy


You want this module: Term::ReadKey.

like image 23
Jonathan Feinberg Avatar answered Sep 19 '22 10:09

Jonathan Feinberg