Java calls shell, zip command in script often fails - java

Java code
try {
String command = "/opt/compress.sh param1 param2 param3";
Process ps = Runtime.getRuntime().exec(command);
ps.waitFor(60 * 5, TimeUnit.SECONDS);
br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
result = sb.toString();
log.info("shell logļ¼š\n" + result);
}finally {
br.close();
}
shell Code
#Other business codes
zip -m -jP $3 ${zipOutPath} ${fileIputPath}
The workflow engine calls jar packages to execute Java code, and the zip command in the shell often fails. Manually executing the JAR package on the server is normal.Zip compresses only a CSV file,the file size is 2G.Try to enable shell Debug, only get -> addling XXx. CSV (%)

You are not consuming output streams properly so this may lead to freeze, and waitFor must be after reading STDOUT. Try using ProcessBuilder instead and see what errors it reports, it may help you to the next step:
ProcessBuilder pb = new ProcessBuilder(cmd);
// No STDERR => merge to STDOUT (or redirect elsewhere)
pb.redirectErrorStream(true);
Process p = pb.start();
try(var merged = p.getInputStream()) {
merged.transferTo(System.out);
}
// Check the executable worked:
int rc = p.waitFor();
if (rc != 0)
throw new RuntimeException("failed: "+Arrays.toString(cmd));

Related

Junit5 is unable to execute shell commands within tests

I had question about running scripts using Junit 5. I have the following piece of code:
public class RunMvnSubprocess {
#Test
public void main() throws IOException, InterruptedException {
String[] cmd = new String[]{"mvn.cmd", "-version"}; // command to be executed on command prompt.
Process p = Runtime.getRuntime().exec(cmd);
try (BufferedReader output = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = output.readLine()) != null) {
System.out.println(line);
}
}
p.waitFor();
}
}
I get no output when I run it using Junit 5.7.0. However, running this on Junit 4.13.2 works fine.
Please note that I am running this piece of test in Windows 10 Pro version 21H1.
EDIT:
Modifying
new String[]{"mvn.cmd", "-version"}
to
new String[]{"cmd", "/c", "\"mvn -version\""}
works for me, but launching a subshell is a bad practice so I am keeping this workaround as a last resort.
Note that you are implicity running a sub-shell as the Windows command CMD.EXE is called to interpret the contents of mvn.cmd, so your value of cmd is equivalent to:
cmd = new String[]{ "cmd", "/c", "call mvn.cmd -version"};
If you get no error code from waitFor or no output or no exception, then the issue will be reported in the STDERR stream. Change to use ProcessBuilder instead and you can merge STDERR to STDOUT as follows:
ProcessBuilder pb = new ProcessBuilder(cmd);
// No STDERR => merge to STDOUT
pb.redirectErrorStream(true);
Process p = pb.start();
Also, no need to write much code to consume STDOUT:
try(var stdo = p.getInputStream()) {
stdo.transferTo(System.out);
}
int rc = p.waitFor();
if (rc != 0) throw new RuntimeException("test failed");
Hopefully this will explain your problem with the mvn command.

Executing Process in java to call external python program but program does not print anything to console

We can use Jython to implement python in java, but I dont want to go for that approach, what I am looking for is using command line utility and fire python command to execute the code and get the console output in java code.
python Main.py < input.txt
I used above command in terminal, it works there, giving me output, but unable to get the output in java code.
Note: Main.py and input.txt and java code in the same folder
What I am doing wrong in java code?
Here is Sample java code which I am calling in order to execute external python code
try {
Process process = Runtime.getRuntime()
.exec("python Main.py < input.txt");
process.waitFor();
System.out.println(process);
StringBuilder output
= new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
System.out.println("here");
int exitVal = process.waitFor();
if (exitVal == 0) {
System.out.println("Success!");
System.out.println(output);
} else {
System.out.println("Process failed");
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
Here is a sample python code:
x = input();
y = input();
print(type(x));
print(type(y));
print(x + y);
here is a sample input file which I am passing as a input to the python code
30
40
As sandip showed, executing a command in java is not the same as running commands through BASH.
At first I tried to execute
bash -c "python Main.py < input.txt" (through java).
For some reason this didn't work, and even if it did its not a great solution as its dependent on the system its running on.
The solution I found to work was by using ProcessBuilder to first make the command, and redirect its input to a file. This allows you to keep the python code unchanged, and for me at least, give the same result as just running the BASH command.
Example:
ProcessBuilder pb = new ProcessBuilder("python3","Main.py");
//Make sure to split up the command and the arguments, this includes options
//I only have python3 on my system, but that shouldn't affect anything
pb.redirectInput(new File("./input.txt"));
System.out.println(pb.command());
Process process = pb.start();
//The rest is the exact same as the code in the question
Heres the ProcessBuilder docs for quick reference
java process not accept < symbol to input file in python command.
Instead you can run like this
python file
f = open("input.txt", "r")
for x in f:
print(type(x));
print(x)
java file
Process process = Runtime.getRuntime().exec("python Main.py input.txt");
process.waitFor();
System.out.println(process);
StringBuilder output
= new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
System.out.println("here");
int exitVal = process.waitFor();
if (exitVal == 0) {
System.out.println("Success!");
System.out.println(output);
} else {
System.out.println("Process failed");
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
and use and same text file.
It should print in console

Grab CloudCompare Command Line Output with Java ProcessBuilder

I'm playing a bit around with a small JAVA gui for the command line mode of cloudcompare.
Therefore I'm using a short snippet like these:
var processBuilder = new ProcessBuilder();
try {
var process = processBuilder
.command("open", "-a", "CloudCompare.app", "-n",
"--args", "-NO_TIMESTAMP", "-C_EXPORT_FMT", "LAS",
"-O", "/Users/se/pcl_1.las",
"-O", "/Users/se/pcl_2.las",
"-MERGE_CLOUDS")
.start();
String error, line;
BufferedReader inputStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = inputStream.readLine()) != null) {
System.out.println("line = " + line);
}
BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((error = errorStream.readLine()) != null) {
System.out.println("error = " + error);
}
var ret = process.waitFor();
System.out.printf("Program exited with code: %d", ret);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
But if I run it on macOS the command line window opens, the process runs normal, but I can't grab any of the informations in it. There is an option to write log files from cloudcompare. That works - the log file shows that all cloud processing steps are done.
Does anybody knows, how to grab the command line output?
As mentioned here, the /usr/bin/open command is not an option to grab the stdinput stream.
I change the command to /Applications/CloudCompare.app/Contents/MacOS/CloudCompare and it works.
The next question is, how to grab the InputStream with a thread. I tried some stack overflow topics, but it doesn't work at the moment, to get the output stream in realtime. It is flushed at the end of the CloudCompare process.

Processes of ProcessBuilder are not writing some output files (Java)

I am running some .jar programs from a Java code. To do that, I use ProcessBuilder, as follows:
ProcessBuilder pb = new ProcessBuilder("java", "-jar", jarFile, configFile);
Process p = pb.start();
finished = p.waitFor(600, TimeUnit.SECONDS);
// print the error output
if(finished){
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
result = builder.toString();
reader.close();
status = p.exitValue();
}
else{
p.destroyForcibly();
result = "Error: maximum time (600 seconds) exceeded.";
}
The above code is within a foor bucle that selects different jar files (using the jarFile variable) at each iteration. Each one of the jar files writes some output files (note that these output files are different from the standard output / standard error of the program, they are other additional output files).
The problem is that, sometimes, some of the jar programs do not create its corresponding files. If I re-run the code several times, the processes that write their output files are different in each execution (which is very rare).
Could someone give me an indication on how to solve this problem? Thanks in advance.

Executing Bash Script Returns Null In Java

I'm using UBUNTU/LINUX
I'm trying to build swing application for myself(this app need to work other platforms too) and i can not execute some commands on java.I tried to execute "java -version"
Here is my code:
Runtime run = Runtime.getRuntime();
Process p = run.exec("java -version");
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
for (String output = br.readLine(); output != null; output = br.readLine()) {
System.out.println(output);
}
} catch (IOException e) {
System.out.println(e);
}
It returns blank page no output!
However i can execute run.exec("ls") / or ("gedit") and so on... and I GET DATA
Also i can execute internal programs that in my computer.
Why i cant execute .sh files or built-in java commands and getting blank page?
You should use ProcessBuilder to mix standard and error outputs:
ProcessBuilder run = new ProcessBuilder("java", "-version");
run.redirectErrorStream(true);
Process p = run.start();
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
for (String output = br.readLine(); output != null; output = br.readLine()) {
System.out.println(output);
}
} catch (IOException e) {
System.out.println(e);
}
Check that java parent directory is in your PATH variable (output System.getenv("PATH")) or use a full path to the binary.
For script, use /bin/sh like that ProcessBuilder run = new ProcessBuilder("/bin/sh", "/path/to/your/script");
All path can be absolute starting with /, or relative (without /) to System.getProperty("user.dir").
The jvm itself directs output to the error stream
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {

Categories