Shell Scripting on OS X with ECMA / JavaScript
Just this week I switched to OS X and found my self wanting to really take advantage of the shell. I have always wanted to be able to do shell scripting in a language that I know well, and I finally found a solution (I know enough bash shell scripting to get in trouble).
You may have heard of the Rhino project. A Mozzila project for creating and maintaining a Java based JavaScript parser. Well, what you may not have know, and what I just found out, is that it also includes a command line javascript interpreter that allows you to write shell scripts in JavaScript.
Since ActionScript is based on ECMA / JavaScript, this means that you can leverage the core of your ActionScript knowledge to write shell scripts on OS X and / or Linux.
Here is what you need to set it up.
First, make sure that you have Java installed and it is in your path (this is already setup on OS X 10.3). You can check by opening a shell / terminal window and typing ‘java’
java
This should output some options and info about Java. If you get an error, install and configure java.
Next, download the Rhino java files from:
http://www.mozilla.org/rhino/download.html
Download the latest version (I have 1.5 R4.1). Once you have downloaded it to your computer, uncompress it. In the root directory, there is a file called js.jar. This contains all of the Rhino code, including the JavaScript command line interpreter. Copy this file into a permanent location.
I put mine in
~/classpath/js.jar
That is:
/Users/mesh/classpath/js.jar
You will probably have to create the classpath folder.
Next, you need to add the path to js.jar to your Java classpath environment variable, so Java can find it when you try to run the interpreter.
I use the bash shell, so I added the following lines to my .bash_profile in my home directory:
PATH="/bin:/sbin:/usr/bin:/usr/sbin"
CLASSPATH="/Users/mesh/classpath:/Users/mesh/classpath/js.jar"
export PATH CLASSPATH
Notice that I point directly to the js.jar file. I also include the directory, but this is not necessary to run the js.jar file. The PATH variable was already defined. Finally, I added CLASSPATH to the export line, so it is available to my environment.
Re-initialize the shell, by either running
source ~/.bash_profile
or just closing and re-opening the shell / terminal window.
At this point, you should be able to run the interpreter. To test, create a file named helloworld.js and add the following code:
print("Hello World");
Now run the script with the following command:
java org.mozilla.javascript.tools.shell.Main helloworld.js
- java : runs the java command
- org.mozilla.javascript.tools.shell.Main : the class that java should run. In this case it is the command line JavaScript interpreter.
- helloworld.js : the JavaScript file that will be run by the interpreter.
This should then print out:
Hello World
Pretty cool heh? Except that is a lot to type in to run one file. Luckily, bash provides an alias command that allows you to create aliases for commands.
So, open the ~/.bash_profile file and add the following line:
alias js='java org.mozilla.javascript.tools.shell.Main'
Save the file, and reinitialize it.
Now, you can run the interpreter like so:
js helloworld.js
You can find complete usage info at:
http://www.mozilla.org/rhino/shell.html
Probably the two most useful commands are:
print() which prints out to the console, and runCommand, which runs a command.
You can also pass command line arguments to the script. Here is a simple command that prints out the number of arguments passed in, as well as their values:
var len = arguments.length;
print("Arg Len : " + len);
for(var i = 0; i < len; i++)
{
print("Arg[" + i + "] = " + arguments[i]);
}
Save this in a file called args.js and run it like so:
js args.js
This will output:
Arg Len : 0
Now run it like so:
js args.js foo bar "foo bar"
which will output:
Arg Len : 3
Arg[0] = foo
Arg[1] = bar
Arg[2] = foo bar
Of course, the real power comes when you start to integrate this with existing unix commands.
I’ll post more examples as I create them, in the mean time if you have any good examples, post them in the comments section.
fyi, here are some sample scripts:
http://www.mozilla.org/rhino/examples.html
The first shows how to manipulate files.
mike chambers
mesh@macromedia.com
mike chambers
6 Dec 03 at 9:47 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
here is how to access environment variables:
http://makeashorterlink.com/?F21113DB6
mike chambers
mesh@macromedia.com
mike chambers
6 Dec 03 at 10:08 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Here is a code snippet that shows how to:
1. call a unix command
2. capture its output in the script
var o = {};
o.output = “”;
var output = runCommand(“ls”,”-l”,o);
print(“—”);
print(o.output);
print(“—”);
You can find more info looking at the java source code for the runCommand method:
http://lxr.mozilla.org/mozilla/source/js/rhino/toolsrc/org/mozilla/javascript/tools/shell/Global.java
mike chambers
mesh@macromedia.com
mike chambers
6 Dec 03 at 11:36 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Hey Mike,
Thanks for posting this. Since I’m too, well, unix-unaware to create tarballs that don’t include the wonderful .ds_store file by hand, I used this make make a little script that’d do it for me, took about 60 lines (and just enough time to get GF irked at me):
function ls(dir) {
var fileAry = new Array();
var fileName = “”;
var type = “”
var typeChar = “”;
var files = {output:”};
var i=0;
runCommand(“ls”, “-F”, dir, files);
fileName = ”;
while (i < files.output.length) {
if (files.output.charCodeAt(i) != 10) {
fileName += files.output.charAt(i);
} else {
typeChar = fileName.charAt(fileName.length – 1);
type=”File”
if (typeChar == ‘*’ || typeChar == ‘%’ || typeChar == ‘=’ || typeChar == ‘|’ || typeChar == ‘/’ || typeChar == ‘@’) {
type = “Directory”;
fileName = fileName.slice(0,fileName.length-1);
}
fileAry.push({file:fileName,type:type});
fileName = ”;
}
i++;
}
return fileAry;
}
function addDir(dir, gen) {
var files = ls(dir);
for (var i=0;i<files.length;i++) {
if (files[i].type == ‘Directory’) {
if (dir != ”)
addDir(dir + ‘/’ + files[i].file);
else
addDir(dir + files[i].file);
} else {
if (dir != ”)
addFile(dir + ‘/’ + files[i].file);
else
addFile(dir + files[i].file);
}
}
}
function addFile(filename) {
if (!created) {
runCommand(“tar”, “cvf”, “cleanTarTemp”, filename);
created = true;
} else
runCommand(“tar”, “rvf”, “cleanTarTemp”, filename);
}
if (arguments.length == 2) {
created = false;
addDir(arguments[1]);
runCommand(“mv”, “cleanTarTemp”, arguments[0]);
runCommand(“gzip”, arguments[0]);
} else {
print(“Syntax: cleantar [archive name] [path]“);
}
Joe Rinehart
6 Dec 03 at 11:47 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Forgot to add syntax (assuming path set up like yours, running script from ~):
js [filename].js docArchives documents
Joe Rinehart
6 Dec 03 at 11:50 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Christian Cantrell sent me the following bash script that uses applescript to speak the first argument passed in.
#!/bin/bash
command=”/usr/bin/osascript -e ‘say \”${1}\”‘”
echo ${command}
#eval ${command}
Here is the same script using the Rhino JavaScript shell
if(arguments.length == 0)
{
print(“Usage : say.js string”);
quit();
}
var say = arguments.join(” “);
var command = “say \”" + say + “\”";
runCommand(“/usr/bin/osascript”, “-e”, command);
mike chambers
mesh@macromedia.com
mike chambers
7 Dec 03 at 12:29 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Joe,
sweet script. It is so cool being able to write this stuff without having to look up the bash syntax all of the time (yes, that means I am lazy).
mike chambers
mesh@macromedia.com
mike chambers
7 Dec 03 at 12:31 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Here is how to make an exectuable script:
–com.js–
#!/usr/bin/java org.mozilla.javascript.tools.shell.Main
print(“Hello World”);
Then make it exectuable:
chmod 755 com.js
and then just run it like so:
./com.js
prints:
Hello World
I think you should be able to just use the alias like so:
#!js
but for some reason my environment variables are not being set correctly. That is why i have the complete paths in the example above.
Now, if I can jsut figure out how to pipe info to it from other programs and access that info from within my shell script I will be set.
mike chambers
mesh@macromedia.com
mike chambers
7 Dec 03 at 1:07 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
That’s neat. I like the idea that you are able to use Java objects within Javascript. Now i don’t have a necessary need for this, i hardly do any Terminal scripting, but i find the idea interesting. It reminds me of some Physics and 3D engines that utilize a scripting language to prevent recompiling the core each time the GL chances.
If you are interested in other general scripting languages on OSX, you might want to have a look at LUA ( http://www.lua.org ) although that might be out of the scope of this project :)
Owen van Dijk
7 Dec 03 at 9:13 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
That’s a cool project, I wish i had discovered it before when starting with SSAS. :)
mack
7 Dec 03 at 9:17 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Great find Mike! This will be very handy i expect.
For those running the T Shell (tcsh), the default on OSX this is how i got it to work.
–
* Follow mikes instructions up until the bash stuff.
* Add the following line to your ~/.tcshrc. As one line:
alias js java -classpath /Users/mesh/classpath/js.jar org.mozilla.javascript.tools.shell.Main
* Then do a source ~/.tcshrc
* Then try js command to see if it works.
–
I ended up adding the class path to the alias as i couldn’t work out how to do it properly :), works fine though and keeps all config in one place so i’ll have more of a chance of understanding it in a years time.
adamh
7 Dec 03 at 8:13 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
nice to know that you can have that on MAc OSX
for people using also Windows you have the Windows Script Host (WSH)
which can launch shell script in JScript/VBscript
access the file system
etc…
you can also launch perl/python
very usefull too
ECMAscript is everywhere ;)
zwetan
8 Dec 03 at 1:02 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Mac OS X automatically looks for jars in your ~/Library/Java/Extensions folder. (If you do not have this folder, simply create it.) Place the Rhino jar there and you can skip the .bash_profile stuff.
Rock
14 Dec 03 at 10:49 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I didn’t know you have that on MAc OSX
Jane Young
16 Dec 03 at 8:50 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
adamh and (t)csh users: the equivalent to the (ba)sh syntax:
FOO=xxx:yyy
export FOO
is
setenv FOO xxx:yyy
In other words, you could put into your .cshrc file:
setenv CLASSPATH $HOME/classpath:$HOME/classpath/js.jar
Michael Khaw
22 Dec 03 at 11:35 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Another cool thing about all this i noticed was the in-built gui debugger. Very cool to be able to step through your scripts.
I added a seperate alias to jsdebug in my shell;
alias jsdebug java -classpath ~/.classpath/js.jar org.mozilla.javascript.tools.debugger.Main
adamh
22 Dec 03 at 6:18 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Shell Scripting on OS X with JavaScript
Mike Chambers: »You may have heard of the Rhino project/a>. A Mozzila project for creating and maintaining a Java based JavaScript parser. Well, what you may not have know, and what I just found out, is that it also includes a command line javas…
Der Schockwellenreiter
23 Dec 03 at 3:34 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
you can simplify the CLASSPATH environmental problem in more recent versions of Java fairly easily. if you *know* that you want a given jar file (like js.jar) to be universally available on your system *without* messing with the CLASSPATH environmental variable, do the following:
open a terminal window, become the superuser, and issue the following commands:
> cd /Library/Java/Home/lib/ext/
> cp path-to-jar-to-install .
(you will need to be su to write to that directory)
I do a *lot* of java development, and I have *no* CLASSPATH environmental variable set. note that java and javac both default to include “.”, the current directory, if there is no CLASSPATH set.
things which I know I want to be generally avail (xml parsers, openGL support, JDBC drivers and so on) go into
/Library/Java/Home/lib/ext/
and when I need to work with some other jar that I do *not* need to be generally *then* it gets added to the CLASSPATH (either at the command-line or in the script that does the call).
jeffs
26 Dec 03 at 12:52 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Rhino is pretty cool and all and it’s nice that you can use Java objects from within Javascript but I prefer SpiderMonkey, the C-based engine (also created by the folks at Mozilla). Like you, I also use a Mac but it’s about as easy to get up and running with SpiderMonkey as with Rhino. Rhino does have a lot going for it but the java overhead bit annoys me a little and it seems like Rhino doesn’t get updated as often as SpiderMonkey.
Here are some instructions for compiling SpiderMonkey on a Mac.
href=”http://majuscule.blogspot.com/2005/11/javascript-adventure.html
gavin
15 Nov 05 at 10:26 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Rhino is pretty nice in that you can talk to Java objects pretty easily. But if you want to run the C-based javascript engine (it’s called Seamonkey and it’s also from Mozilla) it’s not too hard to get it running on OS X. I like the speed of Seamonkey and the fact that I can run it without having to do all the shenanigans involved with running Java apps from the command line. Of course your outlining of the process makes it rather painless. Anyway, since I couldn’t find any instructions on building Seamonkey on OS X, I put up my own:
http://majuscule.blogspot.com/2005/11/compilling-seamonkey-on-os-x.html
gavin
20 Feb 06 at 10:47 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Hi,
I have a query that I have a java class and there are some varible in side java class.I want to know how can I access the variable in side unix shell script,can you please Guide me,I will be grateful to you.
yours truly
Pitabas Pradhan
pitabas pradhan
4 Apr 06 at 10:11 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
@Gavin.
SpiderMonkey is notoriously hard to get File handling to build into it, and also, there seems to be a complete lack of things like runCommand, which are pretty essential for being a command line scripting language. Rhino does this out of the box. I agree that the java overhead makes it a no-no solution, but SpiderMonkey is just no substitute.
GazHay
19 Aug 06 at 1:36 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
MIke thanks for the run through. It actually enabled me to make my long wished for JSLint + Textmate Bundle.
So I can within a textmate file run my jslint bundle with a keyboard shortcut and see the output there and then.
I need to document the process, when I do I’ll pop up a link here.
JP
1 Sep 06 at 12:35 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
For some reason, your instructions for getting Rhino up and running wouldn’t work. However, the simple way of getting it to work is just to drop the js.jar in a folder ~/Library/Java/Extensions (which you may have to create).
ryan
15 Nov 06 at 4:47 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
JP, here is something I wrote up a while back:
http://www.plasticstare.com/plains/2006/11/15/jslint-in-textmate/
This just details running JsLint from textmate.
ryant
16 Oct 07 at 1:08 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I moved the whole rhino directory to ~/.rhino
and then created an executable file under /usr/bin/
and called it “js”, so “/usr/bin/js”
added the following lines
export CLASSPATH=”/Users/kazu/.rhino/js.jar”
java org.mozilla.javascript.tools.shell.Main
and now whenever I do a “js” command
the cli loads without problem!
sweet!
Kazuyoshi Tlacaelel
2 May 08 at 12:06 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Completed the tutorial with success! Now I’m adapting some of my JavaScript apps.
Just like others said, you only need to copy js.jar to the /Library/Java/Extensions folder to use it. Also, for those of you who don’t know how to create an alias (like myself, before googling it to finish this tutorial) – or how to make your terminal understands “js” as “java org.mozilla.javascript.tools.shell.Main”, type in the Terminal:
alias js=”"java org.mozilla.javascript.tools.shell.Main”
Carlos Agarie
16 Aug 11 at 6:19 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>