2

I have a plain java app that has a main class that calls shell script files under src/main/resources/*.sh

I am using Runtime.getRuntime().exec(cmd) to execute the script.

String cmdPath = new this.getClass().getResource("/XXXX.sh").getPath()
String[] cmd = { "bash", cmdPath };

I am getting exit code 127.

1)Running using java -jar xxx.jar

2)File permissions for the sh file is -rw-rw-r--

How can I get the file permission of script file to execute ?

script files are under src/main/resource/

Used maven-jar plugin to bundle the jar and the script files came into root directory

jar /-com -software -cmd.sh

Tried maven assembly plugin with custom script to grant 755 permissions to all files under {basedir} (which is not defined anywhere). It did not work

<assembly
        xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>ourAssembly</id>
    <formats>
        <format>jar</format>
    </formats>

    <fileSets>
        <fileSet>
            <directory>${basedir}</directory>
            <includes>
                <include>**/*</include>
            </includes>
            <fileMode>0755</fileMode>
        </fileSet>
    </fileSets>

2 Answers 2

3

The getPath() method of URL does not return a valid file name. A URL’s “path” is just the portion after the authority, not including any query or fragment. A lot of characters are not allowed in URLs, so they are percent-escaped, and getPath() will return a String with those escapes.

Furthermore, a .jar entry is not a file at all, it’s just a subsequence of bytes in the .jar file representing compressed data. To execute it, you will need to copy it to a true file, and execute that.

Something like this:

Path script = Files.createTempFile(null, ".sh",
    PosixFilePermissions.asFileAttribute(
        PosixFilePermissions.fromString("rw-rw-r--")));

try (InputStream embeddedScript =
    this.getClass().getResourceAsStream("/XXXX.sh")) {

    Files.copy(embeddedScript, script, StandardCopyOption.REPLACE_EXISTING);
}

String[] cmd = { "bash", script.toString() };

You should remove the copy when you’re done with it:

ProcessBuilder builder = new ProcessBuilder(cmd);
builder.inheritIO();

Process process = builder.start();
process.waitFor();

Files.delete(script);
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect! I copied it to a /tmp folder and executed. It worked thanks!
0

If you don't want to create a tempfile for some reason you can also execute the process and pass the InputStream from getResourceAsStream as the stdin.

This is Scala with a slightly different ProcessBuilder API and a funky syntax to pass an inputstream but the same principles apply

import scala.sys.process.Process
object Sh {
  def call(resourcePath: String, args: Seq[String]): Int = {
    (Process("sh", Seq("-s", "--") ++ args) #< getClass.getResourceAsStream(resourcePath)).run().exitValue()
  }
}

Without the -s argument the args to to sh itself, and not your script.

Also, this approach only works for actual shell scripts, if you added #!/usr/bin/python on top sh would ignore it. exec() system call is what processes the #! and we can't call that from Java without JNI or some hacks.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.