I've a Java program to call a python script. I've used exec method. Please find the code snippet below:
Python program (which is to gather a portion of text from wikipedia), when run separately, gives me proper output. When called from Java, I'm not getting the complete output from python program.
I checked the status of BufferedReader Object using ready() method ( as explained here, and the code entered into infinite loop.
I think others also have faced similar problems-https://stackoverflow.com/a/20661352/3409074
Can anyone help me?
public String enhanceData(String name,String entity) {
String s = null;
StringBuffer output = new StringBuffer();
try{
String command="python C://enhancer.py "+name+" "+entity;
Process p = Runtime.getRuntime().exec(command);
BufferedReader stdError=new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
output.append(s);
}
The while loop condition has actually already read a line so you are double reading it for every time in the loop.
while ((s = stdInput.readLine()) != null) {
//s=stdInput.readLine(); <- don't need this
System.out.println(s);
output.append(s);
}
/Nick
Related
I am attempting to generate a list of all installed Programs on my Windows machine.
This is the command I am using:
WMIC /output:D:\miep product get name && type D:\miep > D:\miep_
You might have realized that I'm also trying to make a type-Command as I need the output in UTF-8.
I made a Whitelist for this with a simple loop where I will look later where in my file certain Names will appear and keep them while I remove everything else.
The command works in the command prompt, but when I try to do the same inside my Java Program it keeps telling me I've got an Invalid GET-Expression ...
Here is my function:
void createLists() throws IOException {
//String cmd = "WMIC /output:D:\\miep.csv product get name /format:\"%WINDIR%\\System32\\wbem\\de-DE\\csv.xsl\"";
String cmd = "WMIC /output:D:\\miep product get name && type D:\\miep > D:\\miep_";
System.out.println(cmd);
Process p;
p = Runtime.getRuntime().exec(cmd);
p.getOutputStream().close();
String line;
BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = stdout.readLine()) != null) {
System.out.println(line);
}
stdout.close();
BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((line = stderr.readLine()) != null) {
System.out.println(line);
}
stderr.close();
System.out.println("Done");
}
I also tried the converting stuff with the .csv files as you might have seen in the second line of my code and the same:
Works in CMD, but not in my Java-Program..!
Here it keeps telling me that it's an Invalid XSL-Format
Can someone help?
I have a weird problem when trying to execute a shell command from within a java program. Since there exist thousands of websites that explain how to do it I used the following recommended code:
public String executeShellCommand (String command)
{
try
{
StringBuffer sb = new StringBuffer();
String line = "";
Process p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
p.waitFor();
return sb.toString();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
Acutally, when I try to execute for instance ls -aF is works fine and I get some output as a result. Therefore I'm pretty sure that the above code is, in principal, correct. However, I got another program I'd like to run and that produces a file as an output. I would like to execute it the above way but it never is executed and no output file is generated. Also I do not get any error, warnings or whatsoever in java. When copy and pasting the actual command argument string into the console the execution of the programm/command directly in the shell works fine and the output file is generated. So the command I pass to the method is also correct.
Are there additional things I need to pay attention to when trying to execute a shell command from within java?
UPDATE: I modified my code according to the suggestions. However, it is still hanging:
public String executeShellCommand(List<String> command, String logfile, boolean waitForProcess) { try {
ProcessBuilder pb = new ProcessBuilder(command);
System.out.println("pb.toString() = " + pb.toString());
Process p = pb.start();
System.out.println("2");
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("3");
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new StringBuilder();
String line;
System.out.println("4");
while ((line = err.readLine()) != null) { // <--- code hangs here
errSb.append(line + "\n");
System.out.println("errSb = " + errSb.toString());
}
System.out.println("4a");
while ((line = out.readLine()) != null) {
outSb.append(line + "\n");
System.out.println("outSb = " + outSb.toString());
}
System.out.println("5");
if(waitForProcess) {
System.out.println("Wait for process");
p.waitFor();
} else {
System.out.println("Sleep 5000");
Thread.sleep(5000);
}
System.out.println("6");
//Log result to file
if(logfile != null) {
OutputStreamWriter outWriter = new OutputStreamWriter(new FileOutputStream(logfile));
outWriter.write(errSb.toString());
outWriter.close();
}
return errSb.toString();
} catch(Exception e) { e.printStackTrace(); } return null; }
This will block if your command writes too many characters to stderr. Like for sdtout, Java redirect stderr through a pipe, and if you do not read the pipe, it can fill up and block (size of the pipe is probably less than 256 bytes). To avoid that, you need to read from the Process.getErrorStream(), preferable from another thread as the main thread is busy reading from the Process.getInputStream().
A simpler way to avoid that is to use the ProcessBuilder class instead of Runtime.exec() and ProcessBuilder.redirectErrorStream(true) so that both stdout and stderr are merged into the Process.getInputStream()
As per Process javadoc :
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
You are calling p.waitFor(). If we carefully read the waitFor() documentation:
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
You are waiting for a process which hangs, because its error stream and output stream are never read.
What you should do, is to read these streams:
p.start();
BufferedReader err= new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getOutputStream()));
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new Stringbuilder();
String line;
while ((line = err.readLine()) != null) {
errSb.append(line);
}
while ((line = out.readLine()) != null) {
outSB.append(line);
}
int retCode = p.waitFor(); //0 for success
System.out.println(retCode);
System.err.println(errSB.toString());
You should always read the error stream when calling external programs via the Process class, else you may find yourself in this odd situation where a process hangs forever. (well until someone else -the operating system, another application, etc- kills it, more exactly).
I've also noticed that you use the Runtime.getRuntime() which is not the recommended way to run external programs, starting with java 1.5, as per javadoc:
As of 1.5, ProcessBuilder.start() is the preferred way to create a Process.
ProcessBuilder pb = new ProcessBuilder("ls" , "-aF");
Process p = pb.start();
In the while loop below I want to read only the newest line from Process p's output, ignoring anything else that entered the buffer while the loop was sleeping. How do I do that?
String s;
Runtime r = Runtime.getRuntime();
Process p = r.exec("SomeContinuousProgram");
myInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (true){
if ((s = myInput.readLine()) != null) {
System.out.println(s);
}
Thread.sleep(sleep);
}
You can't "skip" to the newest line that was written from the process. You have to read all lines that came before it.
Split the program into 2 threads. The main thread will read from the BufferedReader and will keep track of what the newest line is. The other thread will sleep, and then display the newest line.
while (true){
if ((s = myInput.readLine()) != null) {
System.out.println(s);
}
This code doesn't make any sense. If readLine() returns null, it is the end of the stream, and the only sensible course is to close the stream and exit the reading loop.
I want to run a Java program from another using ProcessBuilder
I used the code
Process pr = rt.exec("cmd /c cd C:\\Users\\naman\\Desktop & java CalculateSum");
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
BufferedReader error = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
BufferedWriter output= new BufferedWriter(new OutputStreamWriter(pr.getOutputStream()));
String line = null;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
output.write("10");
output.write("30");
while ((line = input.readLine()) != null) {
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exited with error code " + exitVal);
CalculateSum has following code:
System.out.print("Enter 1 st value : ");
a=Integer.parseInt(br.readLine());
System.out.print("\nEnter second number : ");
b=Integer.parseInt(br.readLine());
System.out.println("\nresult is : "+(a+b));
My basic motivation is to run a Java program from another Java program.
NOTE: I don't want to use command line arguments to take input. Also I have tried using ProcessBuilder for the same purpose, but that also did not work.
You could use ExpectJ (http://expectj.sourceforge.net/) to talk to another program using standard input/output.
Use this instead of trixing with BufferedReader/BufferedWriter in your first code block:
ExpectJ expectinator = new ExpectJ(5);
Spawn shell = expectinator.spawn("cmd /c cd C:\\Users\\naman\\Desktop & java CalculateSum");
// Talk to it
shell.expect("Enter 1 st value");
shell.send("10\n");
shell.expect("Enter second value");
shell.send("30\n");
Just blindly guessing at what the issue is, the problem may be flushing.
Try adding System.out.flush(); after each print in the CalculateSum.
In the first program, add newline to your output.write calls, such as output.write("10\n");, and also output.flush(); after that.
First, I saw a few Q's about this issue in the site, but didn't see any answer that solve my problem.
I have a program written in Java and it calls a cmd program written in C++. (this is an assumption since I don't have the actual source) I know the expected I/O of the C++ program, in the cmd it is two lines of output and then it waits for string input.
I know that the first output line of the program is through error stream, and I receive it properly (this is expected), but I don't get the second line in error or input stream.
I tried to write to the program right after the first line ( the error line) and didn't got stuck, but there was no response.
I tried using 3 different threads, for each stream, but again, nothing was received in input/error stream after the first line, and the program didn't respond to writing through output stream.
My initializers are:
Process p = Runtime.getRuntime().exec("c:\\my_prog.exe");
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
Is it possible at all or maybe it depends on the C++ program?
Thanks,
Binyamin
If you want to call native applications like C and C++ from Java, you need to use JNI.
I would suggest to put the input in the program when it has started, it will propably use that as input when it wants it.
Here is how I execute any command line in Java. This command line may execute any program:
private String executionCommandLine(final String cmd) {
StringBuilder returnContent = new StringBuilder();
Process pr;
try {
Runtime rt = Runtime.getRuntime();
pr = rt.exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = null;
while ((line = input.readLine()) != null) {
returnContent.append(line);
}
input.close();
LOG.debug(returnContent.toString());
// return the exit code
pr.waitFor();
} catch (IOException e) {
LOG.error(e.getMessage());
returnContent = new StringBuilder();
} catch (InterruptedException e) {
LOG.error(e.getMessage());
returnContent = new StringBuilder();
}
return returnContent.toString();
}