21

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.

Community
  • 1
  • 1
Cristian
  • 6,765
  • 7
  • 43
  • 64

3 Answers3

11

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:

  1. 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

  2. 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.

  3. 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

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thanks for your answer, I'll see how I can extract the shell script from within the jar file to a temporary location and then extract it from there, do you know how I can do this or know of any links? Thanks for your answer! – Cristian Oct 13 '12 at 17:43
  • As i mentioned elsewhere, you can do: unzip -p JARFILE SCRIPTFILE | bash. This will essentially extract the file from the jar but not actually create a file. – Jay Oct 30 '15 at 14:58
  • 1
    I wish I saw this answer two days earlier :( – Anwarvic Nov 22 '18 at 13:39
  • The first solution is easiest to implement if anyone has to run a shell script from the java program. Here is an example implementation: https://stackoverflow.com/a/56557639/2563765 – Steven Jun 12 '19 at 08:25
5

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());

  }

}
Vlad
  • 18,195
  • 4
  • 41
  • 71
0

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.

rich
  • 18,987
  • 11
  • 75
  • 101
  • That would be good if that jar file was run from the command line but it's a runnable jar file – Cristian Oct 13 '12 at 22:35
  • Executing it via Runtime.exec() should be able to get around that, although it's admittedly ugly. – rich Oct 13 '12 at 22:38