I was trying to execute shell scripts using java code. The following is a sample code to demonstrate the issue :
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("/home/otaku/Programming/data/test1.sh");
try {
Process process = processBuilder.start();
StringBuilder output = new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
int exitVal = process.waitFor();
if (exitVal == 0) {
System.out.println(output);
} else {
System.out.println("Script exited abnormally");
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
The shell script file test1.sh that I am trying to execute :
#!/bin/bash
mkdir -p -- teeh
echo 'Succesfully executed script'
I am getting echo message and was able to print in the java console indicating that the shell script is executed successfully. But no directory is being created even though the command mkdir -p -- teeh is executed. If I manually execute the script file using terminal it works like a charm. I would like to know the reason behind this and a possible solution to this as well.
mkdir -p -- teeh
In this command the teeh path is a relative one rather than an absolute one: it will be created in the script's current working directory.
Your bash script is by default executed with the working directory of your JVM, which depends on where you executed your java application from. If you're executing your code from your IDE, by default this will be the project's root directory. If you're executing from the command line, it will be the directory you execute the java command from.
In any case you shouldn't expect a /home/otaku/Programming/data/teeh directory to be created by your current code unless you run the java application from the /home/otaku/Programming/data/ directory.
There are many possible solutions, whose relevance depend on your context :
execute your java code from the /home/otaku/Programming/data/ directory
use an absolute path in your bash script
use cd in your bash script
use ProcessBuilder.directory(File dir) to execute the bash script with the appropriate working directory
Related
I'm trying to use ProcessBuilder in order to create a tar.zst file that compresses a folder. However, I'm getting the following error after running this code:
try {
ProcessBuilder pb = new ProcessBuilder("tar","--zstd","-cf","info-x.tar.zst","tstpkg");
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Process process = pb.start();
process.waitFor();
} catch (Exception e){
e.printStackTrace();
}
Error:
tar: Can't launch external program: zstd --no-check -3
And the tar file is created, but is empty.
If I run the same command from the terminal
tar --zstd -cf info-x.tar.zst tstpkg
then it works fine.
When you find a command that works "from the terminal" but not from Java then you should assume that the PATH or other environment required with that command is only set correctly via that terminal, and not when called by JVM.
So, one possible fix is to run your terminal indirectly which may fix the environment needed - depending on your shell and it's setup:
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "tar --zstd -cf info-x.tar.zst tstpkg");
You might also try to fix by setting the correct PATH for running zstd as a sub-process of tar. Check where it is using which zstd and try adding to the PATH before pb.start():
String path = pb.environment().get("PATH"); // use Path on Windows
pb.environment().put("PATH", "/path/to/dir/ofzstd"+File.pathSeparator+path);
i have a Java program which executes the following shell script to restart it self.
sleep 5
nohup java -jar /home/my-dir/MyJar.jar &
If i run the script from a terminal, it just works as expected. However if the Java Program executes the script, the program starts normally but nothing gets written to the output file.
I start the script via the following code
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("/bin/sh", "/home/my-dir/start.sh");
try {
processBuilder.start();
logger.info("Successfully started");
} catch (IOException e) {
e.printStackTrace();
}
Following command worked for me. It writes all the output to the specified file.
nohup java -jar /home/my-dir/MyJar.jar > /home/my-dir/log.txt & tail -f /home/my-dir/log.txt &
I am trying to execute "python3 --version" (this is just an example) from Java using ProcessBuilder. python3 is located in /usr/local/bin. I have configured the working directory. Here is my code snippet :
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", "python3 --version");
pb.directory(new File("/usr/local/bin"));
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
while ((line = reader.readLine()) != null)
{
System.out.println(line);
}
reader.close();
But it gives the error : /bin/bash: python3: command not found. Any way to resolve this?
PS : It can execute python --version as it is located in /usr/bin. Rather it executes successfully all commands pertaining to /usr/bin but none of the ones located in /usr/local/bin. python3 is just an instance of the general problem I am facing.
We also have to configure the environment (more so the PATH variable) and append /usr/local/bin as well to it. It will work fine then. I use the Eclipse IDE and I configured the PATH under environment in Run Configurations. It works fine now.
I wrote a bash script. As an example, a really easy on:
#!/bin/bash
gnuplot -e "set terminal pdf; set output'/tmp/test.pdf'; plot [-10:10] sin(x);"
This script shall have the name test.sh .
I can execute this script through my shell and the test.pdf is created. Should I execute this script through a Java application that calls this script through a ProcessBuilder, no pdf is created. The script is accessed and other programs like gedit are opened. Awk etc works. There just seems to be a problem with gnuplot.
Does anyone know why that is and how I could fix this?
The java code is:
String command = "/tmp/test.sh";
try {
ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c",command);
pb.start();
}catch (IOExeption e1) {
e1.printStackTrace()
}
--- Update ---
There seems to be another issue. Thanks to ccarton and muzido the gnuplot script is executed if the java-jar file is executed through the shell. If I embed it into another shell that looks simplified like this execute_jar.sh:
#!/bin/bash
java -jar test.jar
And this execute_jar.sh is executed by another java application that only can execute shell scripts and that I can't modify.
Is there a problem with this parent application? Or is a workaround possible?
The gnuplot script has to be executed through my test.jar because other parameters are generated in the jar and given to the script.
--- Update 2 ---
So I have been searching some more and to test a little bit I changed the test.jar so that it doesn't execute the test.sh but just opens a shell with "xterm". Here I then manually type in the script and I get the error "gnuplot: symbol lookup error: /usr/lib64/libQtSvg.so.4: undefined symbol"
Any ideas?
--- Update 3 ---
I tought it my be a Library problem because the "/usr/lib64/libQtSvg.so.4" exists. So I tried export LD_LIBRARY:$LD_LIBRARY:/usr/lib64 but still the same error.
--- Update 4 ---
I changed the title
add to " end of line.
#!/bin/bash
gnuplot -e "set terminal pdf; set output'/tmp/test.pdf'; plot [-10:10] sin(x);"
to executable file;
chmod +x /tmp/test.sh
this is java code; when run this, test.pdf is created in /tmp/.
public class RunShellScript
{
public static void main(String[] args)
{
String command = "/tmp/test.sh";
try{
ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c",command);
Process p = pb.start();
p.waitFor();
int shellExitStatus = p.exitValue();
System.out.println(shellExitStatus);
}catch(Exception e){
e.printStackTrace();
}
}
}
when run this, what is the java console output?
Scripts are not actually executables. The shebang (#!) is interpreted by your command shell. ProcessBuilder won't do that. To run this from Java you need to execute /bin/bash directly and pass this script as a command line argument.
Example:
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", "test.sh");
In this question it shows that when you run a shell command from Java, it runs from the current directory. When I run the command javac Program.java from my program, it shows the error (from the standard error stream):
javac: file not found: Program.java
Usage: javac <options> <source files>
use -help for a list of possible options
However, when I run the same exact command from the actual Terminal, it works fine and saves the .class file in the default directory. This is the code:
Runtime rt = Runtime.getRuntime();
String command = "javac Program.java";
Process proc = rt.exec(command);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
proc.waitFor();
Any ideas why it works when I type it in the actual Terminal, but not when I run it from my program? I am running a Max OS X Mountain Lion (10.6)
Thanks
You can try to print the path in your programm ,use new File(".").getAbsolutePath() ,if you are in a ide ,the path may be in the root of the project rather than in the path of the current java file
Your program is probably being run from a different directory. Your IDE (such as Eclipse) is probably is running from one location, and knows the directory structure to access your program files.
The easiest, quickest solution is to just write the fully-qualified file path for Program.java.
The alternate is to find out what the current directory is. So, perhaps run pwd the same way you are running javac Program.java from within your program's code? Then you can see what directory your program is actually being run from. Once you know that, you can write the appropriate directory structure.
For example, if pwd reveals that you are actually 2 directories above where Program.java is, then you can put those directories in the command like this: javac ./dir1/dir2/Program.java.
To change the directory Eclipse runs from, see this question Set the execution directory in Eclipse?
The reason my code was not working was because I was running it in Eclipse IDE, and that messes up the directory the program is running from. To fix that program, I changed the command to javac -d . src/Program.java. If I export the program into a .jar file and run it in the desktop, my original command will do just fine.
Thanks to saka1029 for helping me!