Re: Simulating Command Line History in Perl
by Corion (Patriarch) on Apr 23, 2010 at 20:23 UTC
|
| [reply] |
Re: Simulating Command Line History in Perl
by BrowserUk (Patriarch) on Apr 23, 2010 at 20:51 UTC
|
Are you on windows by any chance? Cos Term::ReadLine has never worked for me either. Whether my attempts to use it or when it is embedded in things--like the cpan shell--written by people who should know what they are doing with it.
That said, I find that the windows built-in command line history is sufficient for my needs. Albeit not persistant. If I need to recall commands over more than one re-boot, I put them into a cmd files anyway.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
|
I've always gotten around the issue for MS consoles by switching the input filehandle to read from con.
use Term::ReadLine;
my $cli = Term::ReadLine->new;
if ($^O eq "MSWin32" || $^O eq "dos")
{
my $INPUT;
open($INPUT, "<con")
and $cli->newTTY($INPUT, $cli->OUT);
}
my $line1 = $cli->readline;
my $line2 = $cli->readline; # hit up arrow for line1 history
use Data::Dumper;
print Dumper $line1, $line2;
| [reply] [d/l] [select] |
|
|
Sorry, but you are being fooled by the cmd.exe shell's history facility, into believing that Term::ReadLine is doing something. It isn't.
The following is a completely new command shell where I type 'perl', hit enter, and then paste your snippet. When I get to your prompt I hit the up-arrow key twice. See what is displayed?
Microsoft Windows [Version 6.0.6001]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.
c:\>perl
use Term::ReadLine;
my $cli = Term::ReadLine->new;
if ($^O eq "MSWin32" || $^O eq "dos")
{
my $INPUT;
open($INPUT, "<con")
and $cli->newTTY($INPUT, $cli->OUT);
}
my $line1 = $cli->readline;
my $line2 = $cli->readline; # hit up arrow for line1 history
use Data::Dumper;
print Dumper $line1, $line2;
^Z
INPUT> print Dumper $line1, $line2;
Uparrow twice recalls the 2nd to last line (the last '^z' is displayed on the first uparrow), of input to the command shell. Term::ReadLine cannot possibly be caching this line as it hasn't started running when that is entered.
You can get exactly the same recall without using Term::ReadLien at all. This is another new shell window--note the =comment/=cut lines:
Microsoft Windows [Version 6.0.6001]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.
c:\>perl
=comment
use Term::ReadLine;
my $cli = Term::ReadLine->new;
if ($^O eq "MSWin32" || $^O eq "dos")
{
my $INPUT;
open($INPUT, "<con")
and $cli->newTTY($INPUT, $cli->OUT);
}
my $line1 = $cli->readline;
my $line2 = $cli->readline; # hit up arrow for line1 history
use Data::Dumper;
print Dumper $line1, $line2;
=cut
printf "PROMPT> "; <STDIN>;
^Z
PROMPT> my $line2 = $cli->readline; # hit up arrow for line1 history
Even though the executable part of the program consists of just printf "PROMPT> "; <STDIN>;, I can cycle back and forth through the entire text entered in this shell session.
The first problem with Term::ReadLine (on windows) is that it doesn't use the arrow keys. You have to use some arcane ctrl-alt-meta-key combination to activate it. The second is that is does provide half the functionality of the shell facility.
That's one of the reasons I disable readline support in cpan. It's disrespect for my current/preferred console settings is another
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
|
|
|
|
I heard it can be fixed by setting the TERM env var.
That said, command history now works for me cpan. Maybe it's been fixed? Maybe just in ActivePerl?
| [reply] [d/l] |
Re: Simulating Command Line History in Perl
by almut (Canon) on Apr 23, 2010 at 20:54 UTC
|
I've looked at the Term::Readline::* modules, but none of them seem to do this part of the task.
They do — at least on *nix (preferably install Term::ReadLine::Gnu, which has quite a few more features)
#!/usr/bin/perl
use Term::ReadLine;
my $term = new Term::ReadLine 'Demo';
my $prompt = 'Perl> ';
while ( (my $cmd = $term->readline($prompt)) ne 'quit') {
print "cmd: '$cmd'\n";
}
You can supply a second argument to readline() in case you want to suggest some default input (which can be edited).
| [reply] [d/l] [select] |
|
|
I tried your code but it doesn't give the capabilities to do up and down for the history which is what the op wanted.
| [reply] |
|
|
Works for me. I.e., type "foo" at the prompt, followed by <Enter>. New empty prompt appears. Hit cursor up, and "foo" is back for you to edit. This is what I understood the OP wanted...
If that doesn't work, you're likely either on Windows, or don't have Term::ReadLine::Gnu installed.
| [reply] |
|
|
|
|
|
|
Term::ReadLine will pick from a number of different backends depending on what's available. My custom built perl finds Term::ReadLine::Gnu and works just fine. My system perl, which I never touch, found Term::ReadLine::Stub which does not work correctly, because it's just a stub.
Add this line to almut's code to see your backend.
print "Using ", $term->ReadLine, "\n";
| [reply] [d/l] |
|
|
Like I said, I understand that ReadLine can get the history...that's the easy part. What methods on, say, Term::ReadLine::Gnu, will put the output back on the screen for the user to edit?
| [reply] |
|
|
| [reply] |
|
|
Re: Simulating Command Line History in Perl
by amedico (Sexton) on Apr 23, 2010 at 20:51 UTC
|
The Term::ReadLine module definitely should let you do that. From the documentation, it looks like you need two things:
- The actual C readline library present
- To call the "addhistory" method on input you want saved.
| [reply] |
Re: Simulating Command Line History in Perl
by halfcountplus (Hermit) on Apr 23, 2010 at 21:54 UTC
|
For what it's worth, Term::Readline is a perl frontend to the C library, and readline is definitely the lib that's used by bash (the linux shell) for command history. However, perl modules of this sort do not always provide the same level of functionality (sometimes more, sometimes less) IMO.
In any case, you don't really need to resort to that. I implemented a command history for a perl/Tk app I wrote about 3 years ago. I would do this somewhat differently now, but it's worth noting that this is the source from that and I still use this app on a near daily basis.
A weird and unique thing: this is how I was introduced to OOP (here at perlmonks! ...not bothering to look for thread). If you look at this "function", it's pretty obviously a "class". I know there is some object notation in it, I didn't know that's what I was doing (I understood references). Some perlmonks reg pointed out this should be a package.
Anyway, like I said it still works so is probably a decent source of ideas (ie, please don't blast me for style :lol:)
sub tkhistory {
my $entry = shift;
if ($entry == 0) { # an "unattached" new history
my %hash;
my @list;
$hash{hlist} = \@list;
my $ref = \%hash;
return $ref;}
my $cmand = shift; my $hist;
if ($cmand eq "new") {
my %hash;
my @list;
$hash{hlist} = \@list;
$hash{$entry} = -1;
my $ref = \%hash;
return $ref;
} else {$hist = shift;}
if ($cmand eq "add") {
my $string = $$entry->get;
foreach my $elem (@{$hist->{hlist}}) { # prevent duplic
+ates
if ($string eq $elem) {return 0}}
push(@{$hist->{hlist}},$string);
$hist->{$entry} == $#{$hist->{hlist}};
} elsif ($cmand eq "up") {
if ($#{$hist->{hlist}} == -1) {return}
if ($hist->{$entry} < 0) {$hist->{$entry} = $#{$hist->{hlist}}
+}
else {$hist->{$entry}--};
$$entry->delete('0','end');
$$entry->insert('1',$hist->{hlist}[$hist->{$entry}]);
} elsif ($cmand eq "down") {
$$entry->delete('0','end');
if ($#{$hist->{hlist}} == -1) {return}
if ($hist->{$entry} == $#{$hist->{hlist}}) {
$hist->{$entry} = -1;
return}
$hist->{$entry}++;
$$entry->insert('1',$hist->{hlist}[$hist->{$entry}]);
} elsif ($cmand eq "join") {
$hist->{$entry} = 0;
}
}
I believe "up" and "down" are linked to the key callbacks for various tk entries. "add" is probably linked to the enter key.
The application it's from is here:
http://tkcodex.sourceforge.net/
I mostly use it on C/C++ source trees now.
Good luck! | [reply] [d/l] |