Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Re: Perl as interactive Shell?

by tobyink (Canon)
on May 12, 2017 at 22:05 UTC ( [id://1190166]=note: print w/replies, xml ) Need Help??


in reply to Perl as interactive Shell?

Okay, here's a very basic shell implementation…

use v5.16; package PerlShell { use Moo; use constant { RO => 'ro', RW => 'rw', LAZY => 'lazy', RWP => 'rwp +' }; use Types::Standard -types; use Types::Path::Tiny -types; use Term::ANSIColor (); use Term::ReadLine::Tiny (); use List::Util qw(uniq); use namespace::autoclean; has cwd => ( is => RW, isa => Dir, lazy => 1, builder => sub { Path->class->cwd }, ); has term => ( is => LAZY, isa => InstanceOf['Term::ReadLine::Tiny'], builder => sub { my $self = shift; my $term = 'Term::ReadLine::Tiny'->new; # $term->autocomplete(sub { $self->handle_autocomplete(@_) + }); $term; }, ); has util_namespace => ( is => LAZY, isa => Str, # package name really builder => sub { 'PerlShell::Functions' }, ); sub prompt { my $self = shift; Term::ANSIColor::colored(['bold white'], '> '); } sub prepare_environment { my $self = shift; my ($ns) = @_; no strict 'refs'; my @our; for my $var (keys %ENV) { ${"$ns\::$var"} = $ENV{$var}; push @our, "\$$var"; } ${"$ns\::SHELL"} = $self; push @our, "\$SHELL"; ${"$ns\::TERM"} = $self->term; push @our, "\$TERM"; ${"$ns\::CWD"} = $self->cwd; push @our, "\$CWD"; sprintf('our (%s);', join q[,], uniq @our); } sub format_for_output { my $self = shift; "$_[0]"; } sub run { my $self = shift; my $term = $self->term; my $package = $self->util_namespace; while (defined(my $line = $term->readline($self->prompt))) { local $@; my $prefix = $self->prepare_environment($package); my @output = eval qq{ package $package; no strict; no warn +ings; $prefix; $line }; if ($@) { say {$term->OUT} Term::ANSIColor::colored(['bold red'] +, $@); } else { say {$term->OUT} $self->format_for_output($_) for @out +put; } } say ""; say {$term->OUT} Term::ANSIColor::colored(['bold green'], 'Bye +!'); } } package PerlShell::Util { use Ref::Util (); sub croak { my $fmt = shift; die sprintf("$fmt\n", @_); } sub parse_arguments { my %options; my @arguments; my $seendashdash = 0; if (Ref::Util::is_plain_hashref($_[0])) { %options = %{ +shift }; } while (@_) { if (not Ref::Util::is_ref($_[0]) and not $seendashdash and + $_[0] =~ /\A-([\w-]+)\z/) { my $str = $1; shift; if ($str eq '-') { $seendashdash = 1; } else { $options{$str} = 1; } next; } push @arguments, shift; } return (\%options, \@arguments); } } package PerlShell::Functions { sub args { require Data::Dumper; Data::Dumper::Dumper(PerlShell::Util::parse_arguments(@_)); } sub ls { my ($options, $args) = PerlShell::Util::parse_arguments(@_); my @dirs = map Path::Tiny::->new($_), @$args; @dirs = our $CWD unless @dirs; map { $_->children } @dirs; } sub cd { my ($options, $args) = PerlShell::Util::parse_arguments(@_); PerlShell::Util::croak('cd expects 1 argument, not %d', scalar +(@$args)) unless @$args == 1; my $new; our ($CWD, $SHELL); if ($args->[0] eq '..') { $new = $CWD->parent; } elsif ($args->[0] eq '.') { $new = $CWD; } else { $new = Path::Tiny::->new($args->[0]); if ($new->is_relative) { $new = $CWD->child($new); } } $SHELL->cwd( $CWD = $new ); } } PerlShell->new->run;

It only really implements cd and ls. So you can start the shell and type:

> cd('bin') > ls() > cd('..')

And things work as you might expect. The eval which evaluates each line has strict and warnings switched off, so you can use things like barewords. Thus the following also works fine and feels slightly more shell-like:

> cd bin > ls > cd '..'

Replies are listed 'Best First'.
Re^2: Perl as interactive Shell?
by LanX (Saint) on May 13, 2017 at 13:17 UTC
    Thanks, but i hope you know that there already exist a wild variety of "solutions"

    especially perldebug comes with a variety of unknown goodies, like tab completion, introspection and doc lookup and is backwards compatible.

    I once spend time adding patching the debugger to Data::Dump of the evaled line and allowing multiline commands ( talk ... sorry slides are offline ATM)

    > It only really implements cd and ls. So you can start the shell and type:

    yep, OS independence is nice ... though I'm thinking about reusing Brian's powertools

    > The eval which evaluates each line has strict and warnings switched off, so you can use things like barewords. Thus the following also works fine and feels slightly more shell-like:

    > cd bin

    two problems

    • the Shell code should be production ready, thus allowing cut&paste
    • as soon as you use a path separator like / barewords would fail (parser sees regex)
    my idea is to allow a DWIM typing with barewords and path, but to visually translate to correct syntax

    i.e. one types

    > cd /home/user

    but display and history would automatically shows

    > cd '/home/user'

    (tab completion needs syntax awareness anyway and it'll be a service for people coming from sh)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Ha, I love it! Need a modular Perl Shell? Why not Zoidberg?

      (V) (;,,;) (V)

      Whoop! Whoop! Whoop! Whoop! Whoop! Whoop! Whoop!

      Just another Perl hooker - Working on the corner... corner conditions that is.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1190166]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-19 01:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found