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 :)