Many of you have been making great use of Nashorn’s scripting extensions to do all kinds of amazing things like command script tools and server side JavaScript.  We have been listening to user feedback and decided it was time to step up $EXEC a bit.  We have been testing out some of these features and would like to get some feedback on some of our choices.  Feel free to comment to the group at  nashorn dash dev at opendjk dot java dot net.

 

Background

As a review or for those of you who are unfamiliar, $EXEC is a supplied function that executes command lines from within your scripts.  Note: you need to turn on Nashorn scripting extensions by either using the –scripting command option or by putting a hash (#) as the first character of your script.

$EXEC Example;

print($EXEC("ls -l"))

will print the current working directory's contents (long form.)  You can also use the shortcut (backticks);

print(`ls -l`)

The stdout from the command is returned by the $EXEC call.  You can also fetch output (stdout) from the $OUT global variable.  In addition, the $ERR global contains the error result (stderr) and $EXIT global contains the exit code.  An input string (stdin) can be supplied as a second argument to $EXEC.

Example;

$EXEC("cat", "use cat to print this");
print($OUT);

 

Proposal Rationale

When reviewing the types of $EXEC features users have been requesting, it became clear they were really just trying to embed their favourite shell inside of Nashorn.  It didn't make sense for us create a full shell since users can do the same with the existing $EXEC;

$EXEC("bash", <<EOD);
# declare STRING variable
STRING="Hello World"
#print variable on a screen
echo $STRING
EOD

We also thought about adding $EXEC features using JavaScript syntax;

$EXEC().
cd("~/mydir").
ls("-l");

but thought this form appeared clunky.  And, we also want backward compatibility.  In the end, we just made some very simple changes that we think will meet most needs.

 

Proposed New Features

1. Sensible parsing of command arguments.  The existing $EXEC version uses spaces to identify argument separation.  This is clearly inadequate for file paths and strings.  $EXEC 2.0 handles quoted arguments (single or double quotes) as well as \<sp> (backslash space.)

 

Example;

$EXEC("echo 'my argument has spaces'");

2. Sensible handling of command arguments.  With $EXEC 2.0 it is possible to pass in an array of strings, representing the command and its arguments.  This alternate way of passing arguments avoids any parsing ambiguity.

 

Example;

$EXEC(['echo', 'my argument has spaces']);

 

3. I/O redirection.  With $EXEC 2.0 it is possible to redirect stdin/stdout/stderr from the command line.  The following UNIX style redirects are recognized;

< infile
0< infile
> outfile
>> outfile
1> outfile
1>> outfile
2> errfile
2>> errfile
&> outerrfile
2>&1

Example;

$EXEC("echo 'my argument has spaces' > tmp.txt");

 

4. Multiple commands per call. Each command can be separated by a semicolon (;) or a new line.

 

Example;

$EXEC(<<EOD);
echo this ; echo that
echo whatever you want
EOD

 

5. Piped I/O between commands.  stdout from one command becomes the stdin of the next.  Just insert a modulo (|) character between the commands.

 

Example;

$EXEC("echo 'my argument has spaces' | cat");

 

6. On the fly changing of environment variables.  The builtin commands setenv/unsetenv are interpreted by $EXEC to change the current values of environment variables.  This allows changes to environment variables between commands.  Note that the global $ENV variable captured by $EXEC is unaffected by these local changes.  If you require global changes, then change $ENV before calling $EXEC.  Also note that standard command line environment variable substitutions are not recognized.  You need to use Nashorn template strings instead.

 

Example;

$EXEC("setenv PATH ~/bin:${ENV.PATH}; mycmd");

 

7. Changing current working directory with cd. The builtin command cd is interpreted by $EXEC to change the PWD environment variable (relative to the previous value of PWD.)  The effect is standard cd behaviour.

 

Example;

$EXEC("cd ~/bin; ls -l");

 

Note that this was the number one confusion when using $EXEC.  Many users assume cd is a command, not realizing it is a builtin.

 

8. # style comments.  Characters after # to the end of line are treated as comments.

 

Example;

$EXEC(<<EOD);
# This is a multiline script
# with no commands.
EOD

 

9. Alternate stdin, stdout, stderr.  The second, third and fourth arguments of $EXEC may be java Stream Java objects which are used to redirect stdin, stdout and stderr.

 

Example;

var ByteArrayInputStream = Java.type("java.io.ByteArrayInputStream");
var ByteArrayOutputStream = Java.type("java.io.ByteArrayOuputStream");
var string = `ls -l`;
var instream = new ByteArrayInputStream(string.getBytes());
var outstream = new  ByteArrayOutputStream();
var errstream = new ByteArrayOutputStream();
$EXEC("sed -e '/^d/ d' ", instream, outstream, errstream);
print(outstream.toString());

 

Or an example to inherit I/O from the Nashorn session;

 

var System = Java.type("java.lang.System");
$EXEC("ls -l ; sed -e '/^d/ d' ", System.in, System.out, System.err);

 

10. Special environment variables to control $EXEC.

 

JJS_THROW_ON_EXIT set to 1 will cause $EXEC to throw a range exception if exit code of a command is non-zero.

 

JJS_TIMEOUT  set to a non-zero value will force $EXEC to timeout after that many milliseconds.

 

JJS_ECHO set to 1 will cause $EXEC to echo commands on stdout.

 

JJS_INHERIT_IO set to 1 will cause $EXEC to default to inherit I/O streams used by the Nashorn session (often System.in, System.out, System.err.)