JShell 0.2
Jack Orenstein
Geophile, Inc.
jao@geophile.com

1. What is JShell?
2. Examples
3. Configuring and Running JShell
4. Files
5. Wildcards
6. Variables
7. Strings
8. Piping
9. Redirection
11. Command recall
12. Command usage
13. Extending JShell
14. Known problems
15. Helping out with JShell

# 1. What is JShell?

JShell is a UNIX-like shell written in Java, and intended primarily for working with Java programs. It was written to overcome the problems in working with Java on the Psion 5mx:

• eshell, the EPOC shell which can be used on the 5mx, has no environment variables.
• The command to start a Java program is quite lengthy, and this problem is exacerbated by the lack of environment variables. (Not to mention the 5mx's keyboard, which is good for a PDA but cramped nonetheless.)
• Every time a Java program is started, the Java Virtual Machine has to be loaded. On the 5mx this is time-consuming.
• If you want to compile on the 5mx, javac is your only choice. Because javac is written in Java, loading of the JVM is required. Also, the command for running javac is lengthy.
• Each Java program that executes simultaneously requires another JVM. Not only is there an additional delay as each JVM starts, the memory requirements of each JVM are considerable, especially on a machine with 16 meg RAM. This is a problem if you want to pipe the output from one Java program to another. (I'm not even sure whether eshell supports pipes.)

Because JShell is written in Java, running JShell starts a JVM. This is the only JVM you need. You can use JShell to run other Java programs (i.e. classes with a public static void main(String[]) method), and these run inside JShell's JVM as a thread. The JShell commands are Java classes which also run in the same JVM. You can extend the set of JShell commands. In fact, you are encouraged to do this and distribute your work to other JShell users.

JShell is designed to support development in Java. It is expected that you will be going through edit/compile/run/debug cycles. Java classes other than the JShell commands are reloaded, using a new class loader, each time the command is run. This permits the JShell JVM to work with different versions of the "same" class as you edit and recompile your Java source. You don't need to exit and restart JShell to pick up the latest versions of your Java classes.

While JShell was inspired by the Psion 5mx, it can be used on any Java platform. I've developed and used JShell on Windows NT, Linux, and EPOC.

# 2. Examples

The design of JShell was intended to be familiar to users of shells such as sh and csh. JShell is much simpler although somewhat less powerful. Here are some examples to give you an overview of JShell.

List the files in the current directory, with details on each file:

     5> ls :l


The 5 is a "job number". Each command that you run gets its own job number. The default prompt character is '>'. The ':' character is used for flags, by default. The traditional flag character, '-', is awkward to type on the 5mx while ':' is more accessible, (at least this is true on my 5mx; I've seen other keyboard layouts on other 5mxs). Both the prompt and flag characters can be changed.

List the java sources in the parent directory:

     6> ls ../*.java


List the files in the current directory, recursively, with details:

     7> ls :lR


You can examine the current character used for flags:

     8> env jshell.flag
:


And change it to something else:

     9> set jshell.flag -
jshell.flag=-


Then you can do this, for example:

     10> ls -lR


Here is how to compile all the java sources in this directory:

     11> javac *.java


Type all the .txt files in the current directory:

     12> cat *.txt


Make a new directory, foobar, and copy all the .txt files to it.

     13> mkdir foobar; cp *.txt foobar


Compile a bunch of Java sources in the background.

     14> javac *.java > errors.txt &


An & at the end of the line causes the command-line you entered (including all statements separated by semi-colons) to be executed in the background. You will get a new command prompt immediately after running the above command, but the file files.txt won't be complete until the command finishes executing.

You can view executing jobs using the jobs command, e.g.

        15> jobs
123: javac *.java > errors.txt &


The number before the colon is the "job" number. These numbers are unique within one execution of JShell. You can kill executing jobs by using the kill command. For example, the "javac *.java > errors.txt &" command can be kill like this:

        16> kill 123


# 3. Configuring and Running JShell

## EPOC

JShell.sis should be installed to your D drive. The batch file jshell.bat is written to assume that JShell is installed in D:\Java\JShell. If you don't have a D drive, it is doubtful that you will have enough memory for Java's classes.zip file, the JShell jar files and have enough memory left over to run JShell effectively. However, if you want to try this, you will need to modify jshell.bat and change all the references to the D drive to point to the C drive instead.

To run JShell, you will need to have installed eshell first. You can download eshell from the Symbian web site here.

Once you have eshell and JShell installed, go to the directory D:\Java\JShell. Run the command "jshell". In a few seconds, you should see a console with this prompt:

       0>


The first command will take a few seconds to run as the JShell class files get loaded. Subsequent commands should be faster.

## Windows and UNIX

Add jshell.jar and javacup.jar to your CLASSPATH. Then run jshell as follows:

        java jshell.JShell


# 4. Files

JShell uses UNIX-style file names with forward slashes and no concept of a logical drive (e.g. C: or D:). On DOS-like operating systems, (DOS, any version of Windows, EPOC), the drive letter is just the first part of the file name. So the DOS or EPOC path C:\foo\bar.txt becomes /C/foo/bar.txt. File names are case-sensitive only if the underlying operating system is, (so JShell on DOS and EPOC is case-insensitive, while JShell on UNIX is case-sensitive).

# 5. Wildcards

DOS has two wildcard characters, * which matches any number of characters, and ? which matches any one character. UNIX has two different kinds of wildcarding, "glob" patterns such as foo*.{cc,hh}, and regular expressions.

JShell takes an intermediate position. It uses UNIX-style glob patterns whenever a pattern is required. Regular expressions can be more powerful, but glob patterns should be sufficient for most purposes.

To summarize glob patterns:

 * matches zero or more characters. ? matches exactly one character. [abc] matches a, b or c. [a-zA-Z0-9] matches any letter or digit. {foo,bar} matches foo or bar. xyz matches xyz

For example, this command lists all the .java and .class files in the current directory:

       > ls *.{java,class}


# 6. Variables

A JShell variable begins with a letter or underscore and may consist of letters, underscores, digits and dots. Dots are permitted to provide for namespaces. All variables set by JShell begin with jshell. For example, jshell.prompt is the JShell prompt string.

Variables are set using the set command, e.g.

        100> set FOO xyz


and examined using the env command, e.g.

        101> env FOO
FOO=xyz


or the echo command:

        102> echo $FOO xyz  Note that variable names must be prefixed with$ to obtain the variable's value.

JShell's environment variables include the JVMs System properties. So if you type the "env" command, you'll see values for java.class.path, os.name, and all the other System properties. You'll also see JShell properties such as the current directory (jshell.dir), and whether output pauses after every page of output (jshell.page).

Here is a complete list of the environment variables defined by JShell. (Their values can be obtained by running the command "env 'jshell*'"):

 jshell.buffer Controls whether a buffer is used in handling console output. jshell.columns The number of columns of the output console. jshell.dir The current directory. The value of this variable can be observed and modified using the commands pwd, dirs, cd, pushd, and popd. jshell.flag The character used to denote a flag. jshell.history_size The number of previous commands tracked by JShell. jshell.lines If JShell pauses after each page of output (see jshell.page), then jshell.lines determines how many lines of output constitute a page. jshell.page Indicates whether jshell pauses after each page of output. jshell.prompt The prompt character.

# 7. Strings

Any sequence of characters (possibly including escaped characters) entered on the command line is a string. Strings may be unquoted, quoted with a single-quote character (') or quoted with a double-quote character ("). The rules for processing strings come from UNIX's sh shell:

 Unquoted string Single-quoted string Double-quoted string Escapes Processed Ignored Ignored Glob Patterns Expanded Not expanded Not expanded Environment variables Evaluated Not evaluated Evaluated

Examples:

1) These strings:

        a\bc    'a\bc'  "a\bc"


evaluate to abc, a\bc and a\bc respectively. (The last two strings contain four characters each, and the second character is the backslash.)

2) This command:

        ls *.java


lists all the java sources in the current directory. But these commands:

        ls '*.java'
ls "*.java"


list a file named "*.java" if it exists.

# 8. Piping

UNIX-style piping can be done using the ^ character. (This character was chosen instead of the more traditional | because | is not on the 5mx keyboard.)

# 9. Redirection

UNIX-style redirection can be done for standard output (not error output currently):

 < redirects input. > redirects output, overwriting the target file. >> redirects output, appending to the target file.

A command can be run in the background by typing & at the end of the command. For example, to get a recursive listing of your entire file system in files.txt as a background job, you can do this:

        200> ls :lR > files.txt &


If you enter multiple commands on a line, the entire set of commands runs in the background.

You can observe currently running jobs by using the jobs command, e.g.

        201> jobs
123: javac *.java > errors.txt &
157: ls :lR > files.txt &


You can kill a background job by using the kill command. E.g., here's how you would kill the recursive listing of the file system:

        202> kill 157


There is currently no support for putting a foreground job into the background.

# 11. Command recall

The history command can be used to find out about the last few commands you've executed. E.g.

        3> history
0: pwd
1: cd /foo/bar
2: javac *.java > errs.txt &


You can rerun the previous command by typing !!, or any other command by using it's number, e.g. !3.

By default, the last 100 commands are saved. You can change this limit by modifying the variable jshell.history_size. You can limit the amount of history shown to the n most recent commands by passing n to the history command, e.g.

        301> history 2
299: ls *.java
300: rm *.class


# 12. Command usage

Help on any command can be obtained by using the help command. help takes one argument, the name of the builtin JShell command, (i.e. the name of a class in the package jshell.command). Example:

        400> help cp
cp source ... destination
Copies source files to destination.
- If there are multiple sources, the destination
must be a directory.
- Directories are not copied by default.
- Use the -R flag to copy recursively.

Here is a complete list of the builtin commands:
• cat
• cd
• cp
• dirs
• echo
• env
• exit
• gc
• help
• history
• javac
• jobs
• kill
• ls
• mkdir
• popd
• pushd
• pwd
• rm
• save
• set

# 13. Extending JShell

JShell can run Java applications (i.e. Java classes that have a public static void main(String[] method), and JShell commands. A JShell command is a Java class with the following characteristics:

• It extends jshell.Command.
• It defines two methods:
•                         public void execute(String[] args)
public void usage()

• Input, output and error streams are accessed using the methods in(), out() and err() respectively.
• It calls checkForInterruption() periodically, (so that it can be killed from the command line in a timely manner).

A JShell command can be run by giving the fully qualified class name, followed by its arguments. For example, if you have written a class, com.mycompany.MyClass, which takes one integer argument, you could do this:

        123> com.mycompany.MyClass 419


Command-line arguments (from the JShell command line) are passed to execute's args argument. usage() prints out a usage message.

JShell commands can be connected using pipes, and input and output can be redirected.

Java applications run from JShell are problematic in a few ways. First, they can call System.exit which causes JShell to terminate immediately. Second, piping and redirection are not possible. JShell creates input and output streams for use by commands, but applications use System.in, System.err and System.out directly. It isn't clear how to, for example, pipe the output of one Java application to another when both use System.in and System.out. Another problem is that Java applications may not ever yield control, which means that they cannot be killed. (The kill commands uses Thread.interrupt, not Thread.stop which is deprecated in JDK 1.2 and can produce unpredictable results.)

It is sometimes convenient to use JShell to run Java commands, but in general, if you want to write a class to be run from JShell, you should write it as a command, following the guidelines described above. (Or look at any source in the jshell.command package, to see how JShell's builtin commands are coded.)

# 14. Known problems

• javac flags don't work. You can compile files (e.g. javac *.java), but don't try specifying any flags to the javac command.
• If you run the command "echo $foo" where foo is an undefined variable, echo will simply echo what you type (twice, in fact). This is actually documented behavior (see "help echo"), since "echo$foo" is equivalent to "echo" when foo is undefined. Unfortunately, I know of no way to regain useful control of JShell once this happens. You'll have to kill JShell, e.g. using Shift-Ctrl-Fn-K.

# 15. Helping out with JShell

JShell is an open-source project and contributions are welcome. Contributions can take the form of bug-fixes, new commands, suggestions, new features in the JShell core, etc. I'll act as coordinator and release updates periodically. If you want to help out but don't know what to work on, contact me at jao@geophile.com. If you send me your email address, I'll keep you posted on JShell developments.

Here are some projects that need to be done:

• Platform-specific documentation and help facilities (especially EPOC, but also Windows and Unix).
• Commands:
• mv: Put a general-purpose copy and delete implementation in jshell.OS (and called from jshell.command.mv). Allow OS-specific overrides, e.g. to spawn an mv command.
• grep: based on glob patterns, not regular expressions.
• chmod
• tail
• Get rid of all the CommandElementExpander$Transition* and LexerAutomaton$Transition classes. They greatly increase JShell's footprint. The functionality of these classes could be coded in a much more memory-efficient way, with only a slight loss in program readability.