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

I'm writing a generic user entry routine to capture strings from the user as well as an ending indicator. For example if they type a string and end with [Shift TAB] I will do one thing and if they end with [TAB] or [Enter] I will do something different. I am using the example "How do I read just one key without waiting for a return key?" from perlfaq8. So far so good. Well I quickly discovered that capturing certain keystrokes from a terminal isn't quite as easy as it is in a GUI environment. However I was able to figure out that [Shift TAB] gave me \e[Z. I also wanted to be able to trap for just [ESC] but I settled for [ESC q] instead. Now here's where the weirdness starts. I need to remove the ending character(s) from the string. With [Shift TAB] I was able to use:
$DataRead{'StringRead'} = substr($DataRead{'StringRead'}, -3);
but that didn't work with [ESC q]. Instead I ended up having to use:
$DataRead{'StringRead'} = substr($DataRead{'StringRead'}, 0, length($D +ataRead{'StringRead'}) - 2);
This works fine, but leaves me wondering why the first approach didn't work. BTW Term::HotKey is just the module given in perlfaq8. I'm using perl v5.6.1 in on a Sun OS box using putty as an xterm. My test script follows:
#!/usr/bin/perl use strict; use warnings; use constant true => 1; use constant TAB => 9; use constant LF => 10; use constant CR => 13; use constant ESC => 27; use constant BACKTAB => -1; $| = true; use Term::HotKey; # # ReadFromKeyboard # # Read a key at Time from the Keyboard # # Arguments: # # Returns: \%DataRead # 'StringRead' => Characters Read # 'ExitValue' => Character That Caused End of Read # # Thanks to Liverpole for the Original Version # sub ReadFromKeyboard { my %DataRead; $DataRead{'StringRead'} = ''; my $CharacterRead = ''; while(true) { $CharacterRead = readkey(); if($CharacterRead) { $DataRead{'StringRead'} .= $CharacterRead; print("\[$CharacterRead\]\[" . ord($CharacterRead) . "\]len\[ +" . length($CharacterRead) . "\] \[$DataRead{'StringRead'}\]\n"); if($DataRead{'StringRead'} =~ /.*\t|\n|\r|\e\[Z|\eq$/) { if($DataRead{'StringRead'} =~ /.*\e\[Z$/) { $DataRead{'ExitValue'} = -1; $DataRead{'StringRead'} = substr($DataRead{'StringRead' +}, -3); } elsif($DataRead{'StringRead'} =~ /.*\eq$/) { $DataRead{'ExitValue'} = 27; $DataRead{'StringRead'} = substr($DataRead{'StringRead' +}, 0, length($DataRead{'StringRead'}) - 2); } else { $DataRead{'ExitValue'} = ord(chop($DataRead{'StringRead +'})); } last(); } } } return(\%DataRead); } # Main program my $DataRead; $DataRead->{'StringRead'} = ''; while(uc($DataRead->{'StringRead'}) ne 'QUIT') { $DataRead = ReadFromKeyboard(); my $WhatIsIt; if($DataRead->{'ExitValue'} == TAB) { $WhatIsIt = 'TAB'; } elsif($DataRead->{'ExitValue'} == LF) { $WhatIsIt = 'LF'; } elsif($DataRead->{'ExitValue'} == CR) { $WhatIsIt = 'CR'; } elsif($DataRead->{'ExitValue'} == ESC) { $WhatIsIt = 'ESC'; } elsif($DataRead->{'ExitValue'} == BACKTAB) { $WhatIsIt = 'BACKTAB'; } else { $WhatIsIt = 'IDK'; } print("You Entered: \[$DataRead->{'StringRead'}\]\[" . length($Data +Read->{'StringRead'}) . "\]\nYou Ended with a \[$WhatIsIt\]\[$DataRea +d->{'ExitValue'}\]\n"); } __END__

Replies are listed 'Best First'.
Re: Strange substr behavior
by gone2015 (Deacon) on Nov 20, 2008 at 18:39 UTC

    AFAIK, this:

    $DataRead{'StringRead'} = substr($DataRead{'StringRead'}, -3);
    will set $DataRead{'StringRead'} to the last 3 characters of itself (or all if < 3 characters)... This:
    substr($DataRead{'StringRead'}, -3) = '' ;
    would remove the last three characters (or all if < 3 characters). Shirley ?

    But in any case, would it not be easier to:

    if ($DataRead{'StringRead'} =~ s/(\t|\n|\r|\e\[Z|\eq)\z//) { my $term = $1 ; .... } ;

      Thanks! I realize now that I forgot to include an offset for substr. I'm not sure how it ever worked. Also I like your suggestion.