NAME
psh - Perl Shell
SYNOPSIS
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 -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).
OPTIONS
The following command line options are available:
-w
Enables Perl's warning mode-d
Enables psh's debugging mode-c string
If the -c flag is present, then commands are read fromstring
-r file
If the -r flag is present, initialization commands are not read from the user's .pshrc file but from the specified file
DESCRIPTION
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.
TOKENIZATION
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.
STANDARD EVALUATION STRATEGIES
psh includes the following evaluation strategies:
comment
If the first word of the input line begins with a '#' character, ignore it.
bang
If the first word of the input line begins with a '!' character, send everything after the '!' to system().
built_in
If the first word of the input line matches a psh "built-in" function, call the subroutine associated with that built-in; the subroutine receives a single argument, which is the remainder of the input line exactly as entered.
perlfunc
If the first word of the input line matches either a built-in Perl function, or a defined subroutine in the main:: package, pass the line to eval. If $psh::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."
perlscript
If the first word of the input line matches the name of a file the path $ENV{PATH} which starts with #!/.../perl, and 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.
executable
If the first word of the input line matches the name of an executable file in the path given by $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.
eval
Pass the line to eval, regardless of any condition. This is a catch-all strategy; strategies placed after it will never be triggered.
The list of evaluation strategies to try is kept in the configuration variable $psh::strategies, which defaults to qw(comment bang built_in perlfunc executable eval)
.
BUILT-IN FUNCTIONS
The following functions are provided as built-in functions of psh
. You can add an additional built-in called 'foo' by setting the hash $psh::built_ins{foo} to a reference to a subroutine. When a line like "foo bar $baz" is then evaluated by the 'built_in' strategy, the string 'bar $baz' will be passed to your subroutine as its only argument. This is precisely the difference between making 'foo' a built-in as opposed to simply defining a procedure main::foo and letting the eval strategy call foo: the built-in subroutine will get the precise input string entered, whereas the ordinary procedure will of course get arguments that have already been evaluated by the Perl evaluator. Which you want depends on the nature of the function being defined.
. FILE
[orsource FILE
]Read and process the contents of the given file as a sequence of psh commands.
alias [NAME [=] REPLACEMENT]
Add NAME as a built-in so that NAME <REST_OF_LINE> will execute exactly as if REPLACEMENT <REST_OF_LINE> had been entered. For example, one can execute
alias ls ls -F
to always supply the -F option to "ls". Note the built-in is defined to avoid recursion here. Note also that any previous definition of NAME as a built-in is destroyed by alias, so that "alias source source /home/scripts/" won't work to prepend "/home/scripts/" to every source command.With no arguments, prints out a list of the current aliases.
bg [JOB]
Put a job into the background. If JOB is omitted, uses the highest-numbered stopped job, if any.
cd DIR
Change the working directory to DIR or $ENV{HOME} if DIR is not specified. The special DIR '-' is interpreted as "return to the previous directory'.
exit
Exit out of the shell.
export VAR [=VALUE]
Just like setenv, below, except that it also ties the variable (in the Perl sense) so that subsequent changes to the variable automatically affect the environment. Variables who are lists and appear in %psh::array_exports will also by tied to the array of the same name. Note that the variable must be specified without any Perl specifier like $ or @
fg JOB
Bring a job into the foreground. If JOB is omitted, uses the highest-numbered stopped job, or, failing that, the highest-numbered job.
jobs
List the currently running jobs.
kill [SIGNAL] [%JOB | PID]
Send SIGNAL (which defaults to TERM) to the given process, specified either as a job (%NN) or as a pid (a number).
readline
Prints out information about the current ReadLine module which is being used for command line input. Very rudimentary at present, should be extended to allow rebinding, etc.
setenv NAME [=] VALUE
Sets the environment variable NAME to VALUE.
which COMMAND-LINE
Describe how psh will execute the given COMMAND-LINE, under the current setting of $psh::strategies.
INVOKING PSH
The command-line arguments to psh are:
psh [-d] [-w] [-r RC_FILE] [FILE1 FILE2 ....]
The -w switch runs perl with the "-w"
switch and "use strict;". The -d option puts psh into "debugging" mode, which prints diagnostic output, including primarily the result of the "which" builtin before every execution. Note that you can also enter/leave this debugging mode in a running psh via the $psh::debugging variable.
The -r option specifies a file of commands to be read in and evaluated before processing begins. If it is not set, and $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
.
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.
PSH VARIABLES and FUNCTIONS
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.
- $psh::bin (the basename of the file psh was invoked by)
-
The name of the current shell.
- $psh::cmd
-
The command serial number in the currently-executing processing loop.
- $psh::currently_active (0)
-
The pid of the process psh will currently forward signals to, or 0 if psh will handle the signals internally. Usually 0 unless psh is waiting for a process in the "foreground".
- $psh::debugging (1 if -d option specified, 0 if not)
-
Whether psh's internal debugging output should be produced.
- $psh::echo (0)
-
Controls whether the processing loop saves and displays the Perl value of executing a line of input. Three cases are distinguished: a false value, in which case no results are displayed; a subroutine reference, in which case the subroutine is called with the results (this may be multiple arguments if the eval returned an array) and should return true to display, false to not; or a true (scalar) value, in which case any non-undef, non-empty value is displayed.
In addition to displaying the value, it is pushed onto @psh::val. Note that scalar values are pushed directly onto @psh::val, but array values are pushed by reference.
- $psh::eval_preamble ("package main;")
-
Every Perl expression that psh evaluates as part of its read-eval loop is prepended with this string, intended primarily to set the expected package context.
- $psh::executable_expand_arguments (0)
-
If this variable is true, then the following procedure is applied to each argument ARG of the command line before passing the command line on to "system": If ARG starts with a single quote, it is untouched. If ARG starts with a double quote, it is passed to "eval", and then double quotes are placed around the result. Otherwise, double quotes are placed around ARG, it is passed to eval, and the result is used directly. The intention of these steps is to do variable substitution of perl variables that appear in the command line as the user would expect, and in a way similar to shells such as bash. For example, if @files = (file1, file2, file3), then 'ls @files' will expand to 'ls file1 file2 file3', whereas 'echo "@files"' will expand to 'echo "file1 file2 file3"'.
- $psh::history_file ("$ENV{HOME}/.${bin}_history") [late]
-
The filename psh will use to save the command history in from one invocation to the next, if $psh::save_history is true.
- $psh::history_length (50) [late]
-
The number of lines of command history to keep.
- $psh::host (the output of "hostname -s") [late]
-
The short host name of the machine psh is currently running on.
- $psh::longhost (the output of "hostname") [late]
-
The fully qualified host name of the machine psh is running on.
- $psh::news_file ("$psh::bin.NEWS")
-
A file giving news for the shell.
- $psh::perlfunc_expand_arguments (0)
-
If this variable is true, then command lines interpreted by the perlfunc strategy which contain no parens, or braces or commas except in bash-like brace-expansion expressions, will not be simply evaluated. Rather, they will be interpreted much like traditional shells do: the line will be spilt into words, which undergo globbing, brace expansion, and variable expansion, and the resulting array of words is passed to the specified function as its arguments.
- $psh::prompt ('\s\$') [late]
-
Controls the prompt string in interactive use, see below.
- $psh::save_history (1) [late]
-
If this is true, the command history is saved in $psh::history_file from one invocation of psh to the next.
- $psh::handle_segfaults (0)
-
If set to true, psh will ignore all segfaults and attempt to continue.
- @psh::netprograms ( ping, ftp, ncftp, lynx etc. )
-
Contains names of a number of net based programs for enabling TAB completion of hostnames/bookmarks
- @psh::bookmarks ( 'http://', 'ftp://' )
-
Supposed to contain your most used IP numbers, hostnames or URLs. Those will be eligible for TAB completion if you use one of the programs in @psh::netprograms. Default only contains 'http://' and 'ftp://'.
- @psh::mon ( Jan, Feb, etc. )
-
An array of month names used in printing dates, for example in prompt strings.
- @psh::strategies ( comment, bang, built_in, executable, eval)
-
The list of strategies for executing a line, tried in order.
- @psh::val
-
Stores away the results of executing lines, as described in $psh::echo above.
- @psh::wday ( Mon, Tue, Wed, Thu, Fri, Sat)
-
An array of the weekday names used in printing dates, for example in prompt strings.
- %psh::built_ins
-
The keys are command names, the values are subroutine references which are called with the remainder of the line exactly as input.
- %psh::prompt_vars
-
The keys of this hash are single characters, and the values are subroutine references that implement the given escape character in prompt strings. (See "PROMPT STRINGS" below.)
- %psh::strategy_which
- %psh::strategy_eval
-
These hashes implement the named strategies used in psh's evaluation of input lines. It is possible to add new strategies, see below.
- %psh::array_exports
-
Contains a list of environment variables which should be tied to arrays. The key of the hash is the name of the variable, the value is the delimiter of the list (e.g. ':' in PATH)
- &psh::evl
-
This function takes a string, evaluates it as if it were a line of psh input, and returns the value. Useful in loops like:
psh$ C<for $file (glob $pat) { psh::evl("ls -ld $file"); }>
- &psh::is_number
-
Returns true if its first argument is a number. Intended for use in filter subroutines placed in $psh::echo. For example,
$psh::echo = \&psh::is_number;
will cause only numeric return values to be printed. - &psh::news
-
Returns the current news, as a string.
- * &psh::print_debug, print_error, print_out, print_warning
-
These four functions are called whenever psh wants to produce -d-mode output, error messages, normal output, and warnings, respectively. They could conceivably be redefined to implement logging or similar facilities.
- &psh::symbols
-
Takes one argument, a package name, and shows all of the symbols in that package.
There are other functions in the psh:: package, but they are probably not useful except internally to psh.
PROMPT STRINGS
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:
- d The date in ``Weekday Month Day'' format
- h The hostname
- n A carriage return and line feed
- s The name of the shell
- t The current time in HH:MM:SS format
- u The username of the current user
- w The current working directory
- W The basename of the current working directory
- # The command number of the current command
- $ `#' if the effective UID is zero, else `$'
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.
DEFINING EVALUATION STRATEGIES
There are two hashes, %psh::strategy_which and %psh::strategy_eval. An evaluation strategy called "foo" is implemented by putting a subroutine object in each of these hashes keyed by "foo". The first subroutine should accept a reference to a string (the exact input line) and a reference to an array of strings (the array of "words" in the input line produced by &psh::decompose, provided as a convenience so that each individual strategy doesn't have to recompute this). It should return a string, which should be empty if the strategy does not apply to that input line, and otherwise should be an arbitrary non-null string describing how that strategy applies to that line. It is guaranteed that the string passed in will contain some non-whitespace, and that the first string in the array of words is non-empty.
The $strategy_eval{foo} routine accepts the same two first arguments and a third argument, which is the string returned by $strategy_which{foo}. It should do the evaluation, and return the result. Note that the $strategy_eval function will be evaluated in an array context.
LIMITATIONS
The loop inside psh
will clobber $1
and other variables because it uses matches to implement some of its special functions.
Very little error checking is done. For example, if you evaluate !blork by the bang strategy and there is no command "blork" on your system, you get no feedback whatsoever.
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.
Not exactly a psh
limitation: Term::ReadLine::Gnu seems to be rather buggy on Linux systems and leads to continous crashes of psh
. Use Term::ReadLine::Perl instead ( set PERL_RL to Perl to disable Term::ReadLine::Gnu)
REQUIREMENTS
psh needs several optional Perl modules to offer full functionality:
- Term::ReadLine::Gnu or Term::ReadLine::Perl for readline support (command history, special editing chars etc.). Term::ReadLine::Gnu support is by far more advanced currently than support for Term::ReadLine::Perl
- Term::Size or Term::ReadKey to offer the ability to change the environment variables LINES and COLUMNS when the terminal window size changes while running as standard shell
OTHER PERL SHELLS
Larry Wall exhibits the simple Perl shell while (<>) { eval; print $@; }
on page 161 of the Camel Book (2nd Edition).
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
;
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.
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?
FILES
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.
AUTHOR
Gregor N. Purdy, <gregor@focusresearch.com>
CREDITS
The following people have contributed to the development of psh
:
- Prodigious Contributors
-
Markus Peter <warp@spin.de<> added job and signal handling and filename completion. He also made some updates to history handling.
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.
- ReadLine Support
-
Code examples showing how to apply the Term::ReadLine package were contributed by Billy Naylor <billy.naylor@eu.net> (in his
pash.pl
program, which is his own Perl shell). - Symbol Table Dumping
-
Billy Naylor <billy.naylor@eu.net> also had an example of a symbol table printing function that was used as the starting point for the
psh
functionpsh::symbols()
. Thepsh
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. - Prompt String Variables
-
Matthew D. Allen <s2mdalle@titan.vcu.edu> contributed an enhanced prompt string handling routine that emulates the
bash
prompt variables. This was expanded into the form now present. - Typo Spotting
-
Allan Kelly <akelly@holyrood.ed.ac.uk> found some problems with the generated documentation.
COPYRIGHT
Copyright (C) 1999 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.
3 POD Errors
The following errors were encountered while parsing the POD:
- Around line 33:
You forgot a '=back' before '=head1'
- Around line 434:
Expected text after =item, not a bullet
- Around line 450:
You forgot a '=back' before '=head2'