psh - Perl SHell
psh [options] [file]
Copyright (C) 1999-2000 Gregor N. Purdy. All rights reserved. This script is free software. It may be copied or modified according to the same terms as Perl itself.
psh is a Perl program which executes a read-eval loop with enough options so that general behavior reasonably similar to more traditional shells like 'sh' or 'bash' can be achieved, while still allowing arbitrary perl expressions to be evaluated.
By default within psh, the Perl -w flag and 'use strict' are not
employed so that the user is not bound by their stipulations.
They can both be turned on via a command-line flag; or setting
$^W = 1 will turn on warnings, and calling 'use strict' will
(almost) do the usual thing if called by the user (see LIMITATIONS, below).
Each line of input is read. psh knows a number of possible
strategies for evaluating the line, such as ``send it to system() if it
starts with the name of an executable visible in $ENV{PATH}''. (See
below for a complete list.)
Each strategy in turn (from a user-definable
list) examines the command line to see if it can apply, and the first
matching strategy evaluates the line. There is a psh configuration
variable (see below) which controls whether the perl value of the
evaluation is saved and printed after each command.
psh automatically collects several lines of input into a unit
processed as a single line if there are unfinished Perl constructs on
the line. In particular, if there is an unmatched quote, paren, brace,
or square bracket, input is read until these characters match. If an
input line contains the Perl ``here document'' construct as in
<<XXX, (anywhere on the line), then input is read and
accumulated until XXX occurs on a line by itself. Then the
accumulated input is processed as if it were a single line.
The command-line arguments to psh are:
psh [-d [options]] [-w] [-f RC_FILE] [-c STRING ] [FILE1 FILE2 ....]
They are processed in the following order, regardless of what order they are specified in:
$Psh::debugging variable.
$ENV{HOME} is set, and the file $ENV{HOME}/.pshrc is present, it
will be used. If -r is not specified and the current directory is
different from $ENV{HOME} and it contains a .pshrc file, that
file will be read and executed in addition to
$ENV{HOME}/.pshrc.
string, and then psh exits. In particular, any FILE1
... arguments will be ignored.
If any FILE1 ... arguments are specified on the command line, they will be read and executed and then psh will exit. Otherwise, psh will enter an interactive command loop.
Some evaluation strategies examine the ``words'' of the input. These are produced by a tokenizer which behaves very similarly to traditional shells: words are broken at whitespace, '&' is a metacharacter which means that it always forms its own word, and backslash and double and single quotes act as quoting characters, preventing word breaks at whitespace and the ``meta-ness'' of &.
If the description of the strategy does not mention the ``words'', then the tokenization is irrelevant to that strategy.
psh includes the following evaluation strategies, sorted by the order we suggest for @Psh::strategies. For adding/removing evaluation strategies we suggest the usage of the built-in command ``strategy'' from within psh.
commentbangbracebuilt_inperlfunc$Psh::Strategy::Perlfunc::expand_arguments is
true and the line
contains no parens, or braces or commas (except for {a,b,c} as in
shell brace-expansion), then this strategy tries to
interpret the arguments on the command line in a ``shell-like'' manner:
strings are literal except for variable expansion, brace expansion,
and glob expansion.
The idea of this strategy is to allow perl functions, especially subroutines in main, to be called like ``ordinary commands'' (i.e., executables on disk). Or put another way, the idea is to replace bash's ``shell function'' capacity with ordinary Perl subroutines. The slogan is, ``If the command line looks like an ordinary shell command, interpret it like one, even if the first word is a Perl subroutine.''
auto_resume (not enabled by default)perlscript (not enabled by default)$ENV{PATH}), and (2) that file
starts with #!/.../perl, and (3) that perl is the
same as the Perl under which psh is running, psh will fork and run
the script using the already-loaded Perl interpreter. The idea is to
save the exec half of the fork-exec that the executable strategy would
do; typically the exec is more expensive. Right now this strategy can
only handle the -w command-line switch on the #! line. Note this
strategy only makes sense before the ``executable'' strategy; if it came
after, it could never trigger.
executable$ENV{PATH}, then pass the line to
system. Perl variable substitution will be done on the line first if
the $Psh::executable_expand_arguments configuration variable is
true and the binary which is executed does not match one of the
regular expresions in @Psh::executable_noexpand
fallback_builtinevalThe list of evaluation strategies to try is kept in the configuration
variable @Psh::strategies, which defaults to
qw(comment bang brace built_in executable fallback_builtin eval).
If you want to ensure that ``print'' (for example) refers to the Perl
function ``print'' and not ``/usr/bin/print'', try adding the ``perlfunc''
strategy before ``executable''.
Globbing is used to expand filenames against patterns. Perl Shell understands the sh '*' and '?' globbing characters (where * matches any string and ? matches exactly one character).
In addition, Perl Shell knows the very powerful '**' globbing,
replacing many finds in your daily work. '**' will be replaced
by 'current directories and all sub directories'. For example:
grep foo lib/**/*.pm
will search for foo in all *.pm files which are somewhere (recursivly) within the lib directory.
Pipelines are used to construct processing chains.
cat a.txt b.txt | wc -l
A manifest filter is a chunk of code that causes the creation of a filter process. They are handy for creating simple one-time filters because they don't require creating a program file, setting permissions and so on.
There are three kinds of manifest filters: quick, grep and substitution.
A quick filter consists of a block of code surrounded by curly braces,
with a trailing 'q' modifier. The Perl Shell turns this into a line-by-line
filter. For the code in the braces, $_ will contain the line as it was
read from input (including any end-of-line character). The filter block
should
ls | { print ++$i, ": $_"; }q
A grep filter consists of a block of code surrounded by curly braces,
with a trailing 'g' modifier. The Perl Shell turns this into a line-by-line
filter. Only those lines for which the code in the braces returns a true
value will be printed. For the code in the braces, @_ will contain the
results of splitting $_ with the pattern \s+.
netstat | { $_[1]>2; }g
A substitution filter consists of a perl-style s/// operator instance. The Perl Shell will turn this into a line-by-line filter that performs the substitution on each line, and then prints the line. For example:
ls | s/a/b/
A substitution filter is logically equivalent to a block filter containing the substitution and a statement to print the resulting line. The example above is equivalent to:
ls | { s/a/b/; print; }q
A list of built in functions is available from within psh using
the ``help'' command.
For details about the implementation of built-ins, please see the pshdevel manpage.
psh makes a number of variables and functions accessible to the
user in the Psh:: package for configuration or utility
purposes. Their default values are given in parentheses below. If the
variable is also marked ``[late]'', then it is undefined at the start of
the .pshrc file, but any value given to it during that file will be
used instead of the default setting.
In addition to displaying the value, it is pushed onto the array
determined by $Psh::result_array.
Note that scalar values are pushed directly onto this array, but array
values are pushed by reference.
$Psh::save_history is true.
hostname -s'') [late]hostname'') [late]$Psh::echo
will go. It may be a reference to an array, or the string name of an
array.
$Psh::history_file
from one invocation of psh to the next.
Psh::Util::which is asked to locate a filename in the current
PATH, it will only look for filenames which match this regexp. Names
that do not match this regexp will automatically come back ``not found''.
-A hostname command. psh will
initialize this list with your /etc/hosts file
$Psh::echo above.
$Psh::perl_builtins{grep} = 0; if you don't want ``grep'' to be
treated as a perl function.
Note that the perlfunc strategy will also handle the line if the first word is not present as a key in this hash but a Perl subroutine of that name is defined.
&Psh::evl
C<psh$ for $file (glob $pat) { Psh::evl("ls -ld $file"); }>
&Psh::is_number$Psh::echo = \&Psh::is_number; will cause only numeric return
values to be printed.
&Psh::news&Psh::Util::print_debug, print_error, print_out, print_warning&Psh::symbolsThere are other functions in the psh:: package, but they are probably not useful except internally to psh.
Setting the variable $Psh::prompt to a string will cause that string to be used as the prompt-string. Setting it to a subroutine reference causes the result of running that subroutine to be used each time. For example,
$Psh::prompt = sub { $i++; "psh [$i]\$ "; }
will cause the prompt to be psh [1]$ followed by psh [2]$, and so on.
psh uses some of the same ``prompting variables'' as bash. They are accessed by placing a backslash followed by the code in the prompt string, either hard coded, or as returned by the prompt string function. The variables supported are:
Custom prompting variables may be added by adding entries to the array %Psh::prompt_vars keyed by the single character code. The entries should be subroutine references that return the replacement string.
Due to limitations of the Win32 type of operating system there's no job control available on those systems.
The loop inside psh will clobber $1 and other variables (including possible $_) because it uses matches to implement some of its special functions.
Right now, job control simply assumes that the POSIX interface is fully implemented. There should be a way to turn job control off if this is not the case.
The ``exit status'' of programs invoked in the foreground by the ``executable'' strategy (or even the ``bang'' strategy) isn't available from within psh.
Note that since expressions like 'use foo' return undef when sent to eval(), it is not possible to use that return value as indication of an error. Instead, we use the heuristic that there was no error unless the special Perl variable '$@' is non-empty. Note that the side effects of 'use foo' as a psh command line appear to be exactly as expected.
psh needs several optional Perl modules to offer full functionality:
Larry Wall exhibits the simple Perl shell while (<>) { eval; print $@; } on
page 161 of the Camel Book (2nd Edition).
Lee Eakin <leakin@dfw.nostrum.com> has written the Fancy Poor Man's Perl SHell (called lpsh for Lee's Perl Shell), a simple Perl shell that he has used for a number of years now (it is derived from Larry Wall's Perl Shell). He has added some numeric conversion functions because he often uses it as a calculator.
He has placed it on the web at http://www.dfw.nostrum.com/~leakin/psh (for the
code) and http://www.dfw.nostrum.com/~leakin/psh.README for a short explanation
of the code and a reference to the main Perl Shell site.
Rich Graves <rcgraves@brandeis.edu> posted a comment to the original
psh-0.001 announcement on http://freshmeat.net, which contained this
gem that leverages the Perl debugger: perl -d -e 1;
Hiroo Hayashi <hiroo.hayashi@computer.org> includes perlsh, a ``one-line perl evaluator with line editing function and variable name completion function'' as an example with his Term::ReadLine::Gnu Perl module.
In an example of convergent evolution, at http://jenda.krynicky.cz/
there is a Perl shell module called PSH.pm which is quite similar
to this psh. It is designed to provide a command line that can be called
inside some other program via PSH::prompt();, but a small file
psh.pl is also included that uses PSH to provide a standalone
shell. Perhaps some merger of these efforts would be beneficial to all?
Some versions of the Perl faq mention an interactive Perl shell called
SoftList, which can still be found at
http://www.mit.edu/afs/sipb/contrib/perl/SoftList/. It predates
Term::Readline and was apparently last touched in 1993, so it seems to
be obsolescent.
Tim Newsome, <nuisance@cmu.edu>, has developed a shell he calls
timtosh (There Is More Than One SHell). Per his web site
(http://www.wiw.org/~drz/timtosh),
it is a shell written entirely in Perl. The goal is to get a shell which you
can extend in Perl and can do some other niceties related to
Perl (like perl re file matching). As of 1999-12-13 (Perl Shell 0.004 release date),
Tim says timtosh ``is focused quite differently than psh is, but is currently
still waiting for a rewrite of the command line parsing.
(It has been for almost a year now)''.
Tom Christiansen and Nathan Torkington's book Perl Cookbook, published by O'Reilly in 1998 (ISBN 1-56592-243-3) has ``Example 15-4. vbsh'' on page 531 for section 15.11 (Editing Input). It stands for Very Bad SHell.
As an aid to comparing/contrasting these different shells, here is a brief table indicating whether or not each has certain features.
Key to features:
PE : Perl evaluation of Perl expressions
SHE: shell-like evaluation of shell-like expressions, including
'exec'ing executables searched for in PATH
CLE: command-line editing
JC : job control
PL : pipelines
Key to symbols:
* : feature present
- : feature absent
? : don't know
The shells:
Shell Name PE SHE CLE JC PL
psh (this one) * * * * * Larry Wall shell * - - - - lpsh * - * - - Perl debugger shell * - * - - perlsh * - * - - Krynicky PSH.pm * * ? - ? SoftList * ? * ? ? timtosh - * * * * vbsh ? ? ? ? -
CDPATH - A list of paths of directories in which the cd builtin
should search for its argument. Defaults to ``.''.
FIGNORE - A list (separated by the path separator) of file endings to ignore when performing TAB completion. No default.
HISTSIZE - The maximum number of lines to save in the history file. Defaults to 50.
IGNOREEOF - Controls the action of the shell on receipt of an EOF character as the sole input. If set, the value is the number of consecutive EOF characters typed as the first characters on an input line before bash exits. If the variable exists but does not have a numeric value, the default value is 10. If it does not exist, EOF signifies the end of input to the shell.
LANG - Sets the language of the shell.
SHLVL - The number of shells deep that psh is nested. Initially set to 1. Many other shells do not increment this variable, so it is not authoritative.
PS1 - If $Psh::prompt is unset, psh will try to use this environment variable as prompt.
PS2 - If $Psh::prompt_cont is unset, psh will try to use this variable as continuation prompt.
PSH_TITLE - If $Psh::window_title is unset, psh will try to use this variable as window title.
USER - The name of the current user
psh - The Perl Shell executable script.
.pshrc - The user's Perl Shell `profile'. May be in $HOME or the
current directory; if both are present, both will be read in the order
mentioned.
Gregor N. Purdy, <gregor@focusresearch.com>
The following people have contributed to the development of psh:
Glen Whitney <gwhitney@post.harvard.edu> added evaluation
strategies, improved interrupt/job handling, &Psh::evl, $Psh::echo,
more extensive documentation, and other more minor features.
Omer Shenker <oshenker@iname.com> added file locking, Win32 code, login shell handling, various bits of documentation, and other minor features and updates.
Hiroo Hayashi <hiroo.hayashi@computer.org> added the current, bash compatible support for programmable completions and some small fixes
pash.pl program,
which is his own Perl shell).
psh
function psh::symbols(). The psh version adds the ability to specify a
package name, and it also filters out some special variables. The implementation
technique is also different from Billy's.
bash prompt variables. This
was expanded into the form now present.