When reading input from the user in Perl, how to let Perl not display "Terminating on signal SIGINT(2)"? - perl

I'm very new to Perl and currently I'm learning it on Windows 7 with ActiveState Perl. This is my program.
chomp(my #lines = <STDIN>);
foreach (sort #lines) {
print $_;
}
When running the program, I type some lines of strings then press Ctrl + c to tell the program that I've finished typing. However, after I get my result (generated from print $_;), I also got this message: Terminating on signal SIGINT(2). How to disable this message?
Thanks.

For Windows, to signify End of Line press Ctrl + Z followed by Enter
Pressing Ctrl + C sends the interrupt signal to your program which is the cause of the Terminating on SIGINT message you are seeing.

In Linux, press ctrl + D (EOF) instead of ctrl+C

You can handle signals like that:
$SIG{INT} = sub { print("Caught SIGINT but continue!\n"); };
for a keyboard interrupt. It's an exemple.

Related

perl: can end block be called when program is 'kill'

BEGIN {
while (1) {
print "hi\n";
}
}
END {
print "end is called\n";
}
in shell:
kill <pid>
OUTPUT:
hi
hi
hi
hi
hi
hi
hi
hi
hi
Terminated
The end block didnt get called when i killed it via kill or ctrl-c.
Is there something equivalent that will always get called before program exits
Ctrl C sends a SIGINT to your program. You can 'catch' this with a signal handler by setting the appropriate entry in %SIG. I would note - I don't see why you're using BEGIN that way. BEGIN is a special code block that's called at compile time - at the very first opportunity. That means it's triggered when you run perl -c to validate your code, and as such is really a bad idea to set as an infinite loop. See: perlmod
E.g.
#!/usr/bin/perl
use strict;
use warnings;
$SIG{'INT'} = \&handle_kill;
my $finished = 0;
sub handle_kill {
print "Caught a kill signal\n";
$finished++;
}
while ( not $finished ) {
print "Not finished yet\n";
sleep 1;
}
END {
print "end is called\n";
}
But there's a drawback - some signals you can't trap in this way. See perlipc for more details.
Some signals can be neither trapped nor ignored, such as the KILL and STOP (but not the TSTP) signals. Note that ignoring signals makes them disappear. If you only want them blocked temporarily without them getting lost you'll have to use POSIX' sigprocmask.
By default if you send a kill, then it'll send a SIGTERM. So you may want to override this handler too. However it's typically considered bad to do anything other than exit gracefully with a SIGTERM - it's more acceptable to 'do something' and resume when trapping SIGHUP (Hangup) and SIGINT.
You should note that Perl does 'safe signals' though - and so some system calls won't be interrupted, perl will wait for it to return before processing the signal. That's because bad things can happen if you abort certain operations (like close on a file where you're flushing data might leave it corrupt). Usually that's not a problem, but it's something to be aware of.
put the proper signal handler in your code:
$SIG{INT} = sub { die "Caught a sigint $!" };
the control-c sends the SIGINT signal to the script, who is catched by this handler

Read single characters, and use Return as EOL instead of Ctrl-D in Linux

I'm a beginner to perl, and just started reading user input in my script.
chomp(my $inp = <> );
I have been used to using Return key as the terminator for user input in other languages, and am unsure how to stop reading user input after getting a single key press, or some characters followed by Return key. In perl running on unix, capturing input via the diamond operator, seems to require pressing Ctrl-D for end of input.
My problem is that I'd like to build an interactive menu where user is presented a list and asked to press "A", "B" or "C". Once he presses any of these keys, I'd like to loop according to conditions, without waiting for him to press Ctrl D. How can I get this level of interactive user input in perl? In C, I'd use getch. In Bash, I'd use a read and $REPLY.
I'd also like to know how to use the Return key to terminate user input.
For getting single characters, perldoc mentions:
if ($BSD_STYLE) {
system "stty cbreak </dev/tty >/dev/tty 2>&1";
}
else {
system "stty", '-icanon', 'eol', "\001";
}
$key = getc(STDIN);
if ($BSD_STYLE) {
system "stty -cbreak </dev/tty >/dev/tty 2>&1";
}
else {
system 'stty', 'icanon', 'eol', '^#'; # ASCII NUL
}
print "\n";
Surely in a language like perl, it isnt that difficult?
Edit: It seems like what I was looking for isnt natively available. However, IO::Prompter seems to be the solution.
The diamond operator reads one line in scalar context, and one file in array context. Ctrl-D is EOF, Return is EOL.
Because chomp supplies a list context, you have to break this up:
my $inp = <>;
chomp $inp;
The portable way to read a single keypress is Term::Readkey. See http://learn.perl.org/faq/perlfaq5.html#How-can-I-read-a-single-character-from-a-file-From-the-keyboard-

Perl Term::ReadLine::Gnu Signal Handling Difficulties

I'm using Term::ReadLine::Gnu and have run into a problem with signal handling. Given the script below and a TERM signal sent to the script, the handler for the TERM signal is not triggered until after the enter key is pressed. Using Term::ReadLine:Perl this does not occur.
I've read that Term::ReadLine::Gnu has its own internal signal handlers, but frankly I'm at a loss as to how to work with them.
I've reviewed http://search.cpan.org/~hayashi/Term-ReadLine-Gnu-1.20/Gnu.pm#Term::ReadLine::Gnu_Variables tried setting the rl_catch_signals variable to 0, but that didn't help. Ideally, I'd like to work with the Gnu signal handlers, but I'll settle for disabling them too.
To be absolutely specific, I need the TERM handler to trigger after the signal is received instead of waiting for the enter key to be pressed.
Any help or advice is certainly appreciated!
#!/usr/bin/perl
use strict;
use warnings;
use Term::ReadLine;
$SIG{TERM} = sub { print "I got a TERM\n"; exit; };
my $term = Term::ReadLine->new('Term1');
$term->ornaments(0);
my $prompt = 'cmd> ';
while ( defined (my $cmd = $term->readline($prompt)) ) {
$term->addhistory($cmd) if $cmd !~ /\S||\n/;
chomp($cmd);
if ($cmd =~ /^help$/) {
print "Help Menu\n";
}
else {
print "Nothing\n";
}
}
This is due to perl's default paranoid handling of signals - behind the scenes, perl blocks SIGTERM before starting the readline call and restores it when it's finished. See Deferred Signals in perlipc for the details.
Term::ReadLine::Perl uses perl's IO, which knows about these issues and deals with them, so you don't see this bug with it. Term::ReadLine::Gnu uses the C library, which doesn't, so you do.
You can work around this with one of two methods:
set the environment variable PERL_SIGNALS to unsafe before running the script, as in:
bash$ PERL_SIGNALS=unsafe perl readline-test.pl
Note, BEGIN { $ENV{PERL_SIGNALS} = "unsafe"; } isn't enough, it needs to be set before perl itself starts.
Use POSIX signal functions:
#~ $SIG{TERM} = sub { print "I got a TERM\n"; exit; };
use POSIX;
sigaction SIGTERM, new POSIX::SigAction sub { print "I got a TERM\n"; exit; };
Both the above seem to work in Linux; can't speak for Windows or other unices. Also, both of the above come with risks - see perlipc for the details.

Clear already printed values using perl

I need to clear the printed values in perl console window. For an example,
Note: I am developing this in Windows OS.
use strict;
my $mode;
Initialize();
sub Initialize{
print "Enter 1 or 2";
$mode=<STDIN>;
chomp($mode);
check_mode($mode);
}
sub check_mode{
if(($mode!=1) and ($mode!=2)){
print "invalid selection";
Initialize();
}
else{
print "valid selection";
sleep 5;
}
}
While entering wrong selection, I have called the Initialize function, it is printing again. But, what I want is while calling the function it should delete already printed value in the console window and it should print again. Is it possible?
Please give your valuable suggestions.
While you can use the backspace character code "\b" to erase characters on the current line, that has limitations since when the user hits enter it will print a linefeed and your backspace characters won't carry back up to erase the previous line.
See Win32::Console which should allow you to print your prompt at a fixed location and then later overwrite the wrong selection or you can get input a single character at a time using the InputChar method and suppress the newline...
for specific to window os and linux os
system($^O =~ /win/i ? 'cls' : 'clear');

How can I trap Cntl-C while using Perl's Term::ShellUI?

I used Term::ShellUI and almost every thing
is working as expected but the issue is when I pressed Ctrl-C I want to
print:
Please use ctrl+d to exit the shell
For that I handle the signal but the message print only after I pressed the new line
How to resolve this?
You can do the same without using the IO::Handle library, by setting the $| variable to 1 before printing.
$SIG{INT} = sub {
$| = 1;
print "Please use ctrl+d to exit the shell";
}