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:

#!/usr/bin/jsh

public class ToRun {

    public static void main(String ... args){
        System.out.println("hello world");
    } 
}

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:

#!/bin/bash
#Be aware this code doesn't work don't copy and past :)
javac $1 
java ${$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:

#!/bin/bash
#Get the basic name of the class
TO_EXECUTE=${1%.java}

# create java file removing first line
sed -n '2,$ p' "$TO_EXECUTE.java" > "/tmp/$TO_EXECUTE.java"

# compile the new java file
javac -d "/tmp/" "/tmp/$TO_EXECUTE.java"

# execute the compiled class removing the ./ from the name
java -cp "/tmp/" "${TO_EXECUTE//'./'/''}"

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:

#!/bin/bash
if [ $# == '2' ]
then
    TO_EXECUTE=$2
    VAL=${1//\"/''}
    VAL=${VAL//\'/''}
    CLASS_PATH=".:$VAL:/tmp/"
else
    TO_EXECUTE=$1
    CLASS_PATH=".:/tmp/"
fi

TO_EXECUTE=${1%.java}

# create java file removing first line
sed -n '2,$ p' "$TO_EXECUTE.java" > "/tmp/$TO_EXECUTE.java"

# compile the new java file
javac -d "/tmp/" -cp "$CLASS_PATH" "/tmp/$TO_EXECUTE.java"

# execute the compiled class.
java -cp "/tmp/" "${TO_EXECUTE//'./'/''}"

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

#!/usr/bin/jsh  "/your/path/to/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar"

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import java.util.List;

public class ToRun {

    public static void main(String ... args){
          List<Integer> ints = ImmutableList.of(2,3,0,1,0,1,2,3,2,3);
          for(Integer i :  Ordering.natural().reverse().sortedCopy(ints))
                  System.out.println("hello world "+i);
    }   
}

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

Self Hosting Feed Reader Server
2018-01-02 21:00:00 +0100
Tags: self_hosting

Rust Lang Love
2017-12-28 21:00:00 +0100
Tags: coding, rust

Java Shell Script
2015-05-13 21:00:00 +0100
Tags: java, sh

Self Hosted Calendar Server With Radicale
2015-05-02 21:00:00 +0100
Tags: self_hosting

New Blog
2015-05-02 21:00:00 +0100
Tags: self_hosting

Primitive Boxing Problem
2012-11-20 21:00:00 +0100
Tags: java, coding

DDD Query Way
2012-10-20 21:00:00 +0100
Tags: java, ddd, coding

Refs

Twitter
Github
StackOverflow
GitLab

Projects

Object Query