TGLMAN

Java Shell Script

Often happen that I’ve to write some shell/automation scripts for job or fun and most of the time i do it with bash or similar, but because my main programming language has been java, i every time wondered if could be possible do some of this script in java, i know that a lot of other languages have the support for be ran as script, like in D you can put on the top of your file #!/usr/bin/env rdmd and that file can be compiled or just run, in case you run it it will compile itself and exec the code.

Also most of the scripting languages have the possibility to be run directly ex: ruby file can start with #!/usr/bin/env ruby , python file with #!/usr/bin/env python etc.

So I started to think how to do the same thing in java, I’ve to admit, i didn’t search much if something was already done, was more fun do it :).
The plan is simple, I’ve got a java file (or almost) i just need to compile it and run the compiled, it shouldn’t be too hard to do :).

So I created a pseudo java file:

1#!/usr/bin/jsh
2
3public class ToRun {
4
5        public static void main(String ... args){
6                System.out.println("hello world");
7        } 
8}

in this way an executable in the path /usr/bin/jsh will be executed and will receive as parameter the path of the java file.
so now let’s write this executable that can be a script itself something similar to:

1#!/bin/bash
2#Be aware this code doesn't work don't copy and past :)
3javac $1 
4java ${$1%.java}

this should compile and run the java, but it doesn’t, because the first line of our java file is not valid java code, the real problem is that is not there(or at least i didn’t find it) a way to write the first line of the file for make the script be executed and java not complain about the syntax, so the only solution is manipulate the content of the script for remove the first line and generate a new file that is a valid java file.

For do that i can used some tool that Linux provide, in particular i used sed here the script evolved:

 1#!/bin/bash
 2#Get the basic name of the class
 3TO_EXECUTE=${1%.java}
 4
 5# create java file removing first line
 6sed -n '2,$ p' "$TO_EXECUTE.java" > "/tmp/$TO_EXECUTE.java"
 7
 8# compile the new java file
 9javac -d "/tmp/" "/tmp/$TO_EXECUTE.java"
10
11# execute the compiled class removing the ./ from the name
12java -cp "/tmp/" "${TO_EXECUTE//'./'/''}"
13

This worked if you want have fun you can just copy this example script in /usr/bin/jsh and run the previous java file doing just ‘./ToRun.java’.

But anyway I was not completely satisfied, if you used java you know that it can do a lot, but not enough without using some library, so i needed to add a way to define a class path.
Exploring a bit i found out that if you add some additional parameter on the first line of the script this parameter are send to the script, and at the end is appended the script name, so i used this to configure the classpath

Here the script with the support of the classpath:

 1#!/bin/bash
 2if [ $# == '2' ]
 3then
 4        TO_EXECUTE=$2
 5        VAL=${1//\"/''}
 6        VAL=${VAL//\'/''}
 7        CLASS_PATH=".:$VAL:/tmp/"
 8else
 9        TO_EXECUTE=$1
10        CLASS_PATH=".:/tmp/"
11fi
12
13TO_EXECUTE=${1%.java}
14
15# create java file removing first line
16sed -n '2,$ p' "$TO_EXECUTE.java" > "/tmp/$TO_EXECUTE.java"
17
18# compile the new java file
19javac -d "/tmp/" -cp "$CLASS_PATH" "/tmp/$TO_EXECUTE.java"
20
21# execute the compiled class.
22java -cp "/tmp/" "${TO_EXECUTE//'./'/''}"
23

and here an example of a simple script that use guava collection:

 1#!/usr/bin/jsh  "/your/path/to/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar"
 2
 3import com.google.common.collect.ImmutableList;
 4import com.google.common.collect.Ordering;
 5import java.util.List;
 6
 7public class ToRun {
 8
 9        public static void main(String ... args){
10                  List<Integer> ints = ImmutableList.of(2,3,0,1,0,1,2,3,2,3);
11                  for(Integer i :  Ordering.natural().reverse().sortedCopy(ints))
12                          System.out.println("hello world "+i);
13        }   
14}

this sorted out my needs, but while i was writing this post i noticed that eventual parameter of the script are not passed forward, it’s not a big issue but maybe with some refactoring of this script can be supported also that.

If you want to use this script don’t copy and past the examples, but here there is a final version, where is possible to configure a bit the script, and also the temporary file are managed in a better way.

From now you can enjoy your script written in java :)

Posts

Java Shell Script
13 May 2015
Tags: ,

Self Hosted Calendar Server With Radicale
2 May 2015
Tags:

New Blog
14 April 2014
Tags:

Primitive Boxing Problem
20 November 2012
Tags: ,

DDD Query Way
20 October 2012
Tags: , ,

Refs

Twitter
Github
StackOverflow
OpenHub
SourceForge

Projects

Object Query