1

In Unix (or Linux), if I want to run a shell script, I can start the file with #!/bin/sh. With awk, I start the executable file with #!/usr/bin/awk -f and it treats the rest of the file as the program.

How do I do that with a Java program? I tried copying the simple.class to simple, putting #!/export/appl/Mail/java/bin/java at the top and making the file executable, but I get:

69> ./simple
Error: Could not find or load main class ..simple

I know this can be done with an executable shell script, or a C program that execs the java interpreter. Every other interpreter on Unix can be called with a #! load card, surely there's a way to do it with Java.

chrisaycock
  • 36,470
  • 14
  • 88
  • 125

7 Answers7

1

The most usual way is to have a wrapper for the Java. A shell script that executes the "java -jar yourJar.jar" or equivalent. And then you bundle the shell script and the windows equivalent bat file with your product.

Another option is to have a native launcher. For example you can see the Eclipse project which has gone that way. You download Eclipse and you have a native executable to run. The native executable will launch your Java program.

One more option is to compile Java into native code. For example you can use this commercial tool called Excelsior JET ( http://www.excelsior-usa.com/jet.html ).

toomasr
  • 4,731
  • 2
  • 33
  • 36
1

The Java class file format doesn't allow text before the header, that's why the Java runtime no longer accepts the .class file after your modification.

On Linux you can use binfmt_misc to support additional executable formats, including .class files. It's basically a way to tell the Linux kernel how to detect executable formats and how to execute them.

This Archilinux Wiki article explains in more detail how to get this effect.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • Looks like binfmt_misc still starts a shell instance to process the .jar file. – user2456198 Jun 05 '13 at 16:15
  • @user2456198: you can configure it to start anything you want. But since the `java` executable won't accept a class file name directly, you'll need to use something else. That could be a C program that starts the JVM via the API or a shell script. – Joachim Sauer Jun 06 '13 at 06:51
0

You cannot do it with a Java program. Firstly, the Java program needs to be compiled before execution. Secondly, even if compilation wasn't required, the hash sign is not a comment in Java, so that would be a syntax error.

piokuc
  • 25,594
  • 11
  • 72
  • 102
  • Both those apply to C programs, but I can compile and create executables in C. – user2456198 Jun 05 '13 at 15:26
  • @user2456198 Actually no, you tell the C compiler to produce an executable program. If you took a random compiled C object file or .so library and added a hashbang directive to the top, and modified it through chmod +x it wouldn't suddenly become executable. – Nate Jun 05 '13 at 15:37
0

I've never heard the term "load card". What you have is an "interpreter directive" designated by a shebang. This merely designates which interpreter the shell should invoke on a given script.

As for why C programs can be run directly in the shell, executables recognized by the operating system are passed to the loader. A Java class isn't an executable, at least to the OS anyway. So the shell must know which interpreter to pass control to.

But as you've noticed, the shebang doesn't work. The reason is that the class file is in a specific binary format that the JVM expects. Editing this file will break convention and lead to an error. Therefore, there is no way to do what you've asked.

However, you can create a "shortcut" to the program you want to run by creating an alias or perhaps writing a one-line Shell script to wrap the java command you need. This is the common practice as I understand it.

chrisaycock
  • 36,470
  • 14
  • 88
  • 125
  • Back in the day when dinosaurs ruled the earth, programs used to be written on pieces of cardboard referred to as "punch cards". The 1st card in a stack was a "load card", and determined the program used to interpret the rest of the stack. It could be a compliler like Fortran or an interpreter like rexx. Unix starts executables with a magic number. If the 1st two bytes are #!, Unix runs the rest of the line as an program and arguments/options. Javac is a compiler and java is an interpreter. Every other Unix interpreter has a way to read the rest of the file as input. – user2456198 Jun 05 '13 at 16:08
  • 1
    @user2456198 But the [Java class file has a magic number 0xCAFEBABE](http://en.wikipedia.org/wiki/Java_class_file#Magic_Number). If you modify a class file to have a shebang, then wouldn't that break the JVM's expectations? Also, I notice that in your question you renamed the class file to remove the suffix. The JVM has very specific assumptions about the file name that you have broken. – chrisaycock Jun 05 '13 at 16:37
0

The other answers explain why you can't do what you are trying to do. However, if your shell is zsh, you can create a suffix alias. For example, to execute .jar files using java:

alias -s .jar="/usr/bin/java -jar"

Then, to execute blarg.jar, you just type ./blarg.jar. Of course, you must chmod +x your .jar file first.

Markku K.
  • 3,840
  • 19
  • 20
0

Apart from the wrapper script and binfmt_misc solutions suggested by others, I'd like to suggest a potential solution which doesn't directly answer your question but maybe it solves your actual problem.

Since Scala does have an interpreter that can run code without you having to compile it first, and this code can reference any Java code, if your goal can be summed up as "using Java as a shell scripting language", you could use a .scala file as your starter script (which can include the shebang to be run with scala) from which you can call all your Java classes. This isn't any simpler tha having a bash-based starter script, but it's a good starting point to gradually move to scripting in Scala instead of Java in which case you can get rid of the need to compile to .class file in the first place.

Michał Kosmulski
  • 9,855
  • 1
  • 32
  • 51
-1

The reason this doesn't work is that Java isn't really an interpreted language, it's partially compiled, partially interpreted.

The .java source code that you'd put the hashbang directive in needs to be compiled to a .class file before the java interpreter can run it. Comments are stripped out by the compiler, so there's no way to push a comment from the .java into the .class file. The .class file is "compiled" output in a specific format, so adding a hashbang directive to the top of it would break the format.

Java isn't really meant to be a scripting language - but some JVM languages are. For example Groovy supports hashbang and so does Clojure.

Community
  • 1
  • 1
Nate
  • 16,748
  • 5
  • 45
  • 59
  • The interpeted/compiled distinction has no influence here. If the .class file specification allowed random text before the header, then this could work (assuming you wrote a launcher script that accepted file names instead of class names). – Joachim Sauer Jun 05 '13 at 15:44
  • @JoachimSauer "Comments are stripped out by the compiler" is why the interpreted/compiled distinction is brought up. – Nate Jun 05 '13 at 16:00