Possible Duplicate:
Executing a shell script inside a jar file. How to extract?
I have a shell script packaged into my JAR file within the 'engine' package.
In my program I am running a shell command using Process and ProcessBuilder. This all works fine.
If I specify the path to the shell script on my computer then the program works fine. If however I package the shell script into my JAR and access it like this:
scriptLocation = this.getClass().getResource("/engine/shell-script.sh").toString();
And run the program, then I get the following error:
java.io.IOException: Cannot run program "file:/Users/USERNAME/Desktop/Application-Name.jar!/engine/shell-script.sh": error=2, No such file or directory
at java.lang.ProcessBuilder.start(ProcessBuilder.java:460)
at java.lang.Runtime.exec(Runtime.java:593)
at java.lang.Runtime.exec(Runtime.java:431)
at java.lang.Runtime.exec(Runtime.java:328)
at engine.Download$1.run(Download.java:39)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.io.IOException: error=2, No such file or directory
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:53)
at java.lang.ProcessImpl.start(ProcessImpl.java:91)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:453)
... 5 more
I'm starting to think this cannot be done, I would very much like to provide this shell-script as part of the application, can it be done?
Thanks in advance everyone.
======== UPDATE ==========
I eventually solved this problem. Shell cannot execute scripts within a JAR bundle, it's not about Java, but shell. I fixed this by doing the following:
Java has a createTempFile() method (with option to delete the file once the application has been terminated), invoke this, and write the file within the JAR bundle that you want to access to this temp file. You then have the shell script on the local filesystem, and are able to execute the script.
Its not about Java, its about shell. As far as I know shell interpreter can't execute scripts that reside in the zip file. So you have a couple of options here:
Read the shell script as a text file from within your java program.
create a temporal file (in temp dir or any other relevant place and copy the content of the file there. And then call the shell interpreter with this temporal script.
I believe its the best approach
Since jar is a zip, you can unzip it from within the shell find a script and execute the shell. Again this is more awkward and rather wrong, but technically it should work.
If you don't have low level stuff in your script, you can consider to rewrite the logic to groovy script (maybe you'll find useful groosh project).
Then call the groovy code from the memory and not from the file.
Well, I'm out of ideas, If I were you, I would implement the first solution :)
Good luck and hope this helps
The simplest solution would be to have your application extract the shell script into a temporary location and execute it from there.
You can produce a temporary file in Java by calling File.createTempFile(String, String)
.
Another thing that you could possibly do is pass the contents of the script, as loaded from your jar, as the standard input of the shell.
As a simplification, if I had an archive a.zip
containing a script a.sh
, this would execute it:
$ unzip -p a.zip a.sh | bash
From a Java program, you could do the following:
import java.io.*;
class Test{
public static void main(String[]args) throws Exception {
StringBuilder sb = new StringBuilder();
// You're not strictly speaking executing a shell script but
// rather programatically typing commands into the shell
// sb.append("#!/bin/bash\n");
sb.append("echo Hello world!\n");
sb.append("cd /tmp\n");
sb.append("echo current directory: `pwd`\n");
// Start the shell
ProcessBuilder pb = new ProcessBuilder("/bin/bash");
Process bash = pb.start();
// Pass commands to the shell
PrintStream ps = new PrintStream(bash.getOutputStream());
ps.println(sb);
ps.close();
// Get an InputStream for the stdout of the shell
BufferedReader br = new BufferedReader(
new InputStreamReader(bash.getInputStream()));
// Retrieve and print output
String line;
while (null != (line = br.readLine())) {
System.out.println("> "+line);
}
br.close();
// Make sure the shell has terminated, print out exit value
System.out.println("Exit code: " + bash.waitFor());
}
}
I usually just do...
$ jar xf myapp.jar run.sh && ./run.sh && rm run.sh
...which you could call from a Runtime.exec() if necessary.