BufferedReader print input while reading in realtime - java

my java method looks like this:
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
String line = "";
Process proc;
try {
proc = Runtime.getRuntime().exec(command);
InputStream inputStream = proc.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
If I send small commands like "ls -l" it executes the command and print the results. But I need this function to read the output from an application wich will run for days and it will print frequently output while running. So I can't wait until the application is "done", I need the output in realtime. Anyone knows how to print the output without delay? thanks for help..

The simplest way is to redirect the output of the command directly to a file:
ls -l > ./ls.output
In this case you don't need to wait the end of the command. It will be the operating system to handle the output redirection to a file.
If you need also to write the output for the error you need a command like the following:
ls -l > ./ls.output 2> ./ls.err
where ./ls.output is the normal output and ./ls.err is the output for errors

Related

Shell command not executed

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

Control a command line program session via Java

I have a computer algebra program (called Reduce) that works in the shell in an interactive manner: launch Reduce in the shell, then you can define variables, compute this and that, and what not. Reduce prints the output into the shell. My idea is that I want to build a frontend for this text-based program that evaluates its output and converts it into a nice LaTeX style formula. For this I want to use Java.
I can start Reduce via exec(). But how can I emulate text input to the opened shell, and how can I read back what Reduce writes into the shell?
Thanks
Jens
Edit 1: Current Code
// get the shell
Runtime rt = Runtime.getRuntime();
// execute reduce
String[] commands = {"D:/Programme/Reduce/reduce.com", "", ""};
Process proc = null;
try {
proc = rt.exec(commands);
} catch (Exception e) {
System.out.println("Error!\n");
}
// get the associated input / output / error streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedWriter stdOutput = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
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;
try {
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
} catch (Exception e) {
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
try {
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
} catch (Exception e) {
}
You need to get the streams associated with the process including the InputStream, OutputStream, and ErrorStream. You then can send messages to the process via the OutputStream and then read info from the process via the InputStream and the ErrorStream.
From some code of mine:
final ProcessBuilder pBuilder = new ProcessBuilder(TEST_PROCESS_ARRAY);
final Process proc = pBuilder.start();
procInputStream = proc.getInputStream();
errorStream = proc.getErrorStream();
errorSBuffer = new StringBuffer();
streamGobblerSb = new StreamGobblerSb(errorStream, "Autoit Error", errorSBuffer);
new Thread(streamGobblerSb).start();
final Scanner scan = new Scanner(procInputStream);
You may want to look into using the Process class.
http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html
I believe you may be able to start the process, and then use getOutputStream() to feed commands into the process.
While this is not strictly an answer, I discovered that it is more convenient for me to stick with PHP's function proc_open(). That way I can include the output directly in the frontend and do not need to worry about the communication between my Java program and the html frontend.
For everybody who wants to stick to the Java method: the article http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html is a good reference.

Not able to capture output of "who -m" command in JAVA

I want to get the IP address of logged in user of the telnet session
So I have written the following code:
Process p1 = Runtime.getRuntime().exec(new String[] {"/usr/bin/who", "-m"});
p1.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p1.getInputStream()));
String line = reader.readLine();
System.out.println("line=" + line);
reader.close();
But I am getting the output as "null".
Where as expected output is:
linus pts/1 Dec 10 03:48 (172.21.235.48)
In this case you shouldn't pass the -m option.
This works for me :
Process p1 = Runtime.getRuntime().exec(new String[] {"/usr/bin/who"});
Try to consume the input stream from the process before calling waitFor().
You can create a shell script which will receive parameter (option) from your java program.
Then you can run your shell script from java like this -
ProcessBuilder pb = new ProcessBuilder("/PATH/test.sh","-m");
String line;
Process process=pb.start();
java.io.InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
while ((line = br.readLine()) != null)
{
System.out.println(line);
}
return br;
Now in test.sh, you cab grab the argument and run the command -
/usr/bin/who $1 (needs to check, not sure)
Hope this helps.
Thanks guys for the replies.
From the reply of "dystroy" I understood that -m was creating problem.
I tried --m and it worked :)
Please try this code. It's working for me and returns the same output as who -m UNIX command.
import java.io.*;
public class UserPB
{
public static void main(String[] args)
{
try {
// ProcessBuilder pb = new ProcessBuilder("/u01/app/chdir/user.sh");
ProcessBuilder pb = new ProcessBuilder("who");
Process p;
p = pb.start();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("Your Host Details--->"+input.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

I want realtime output of my Runtime.getRuntime().exec()

public static void executeCommand(String cmd) {
try {
Process process = Runtime.getRuntime().exec(cmd, null,
new File("/usr/hadoop-0.20.2/"));
InputStream stdin = process.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line;
System.out.println("<output></output>");
while ((line = br.readLine()) != null)
System.out.println(line);
InputStreamReader esr = new InputStreamReader(
process.getErrorStream());
BufferedReader errorReader = new BufferedReader(esr);
String lineError;
while ((lineError = errorReader.readLine()) != null)
System.out.println(lineError);
process.waitFor();
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
}
Here's my code for executing a command named 'cmd'. But I cannot get realtime output through this code. The output comes out when the command finishes. I want realtime output. Is there a way to do this?
The issue you describe is most likely caused by the application you called: many applications use unbuffered I/O when connected to a terminal, but bufferen I/O when connected to a pipe. So your cmd may simply decide not to write its output in small bits, but instead in huge chunks. The proper fix is to adjust the command, to flush its output at the appropriate times. There is little you can do about this on the Java side. See also this answer.
I think you need to have a thread for handling the output.
You should try first with the cmd which run for a while
Last time, when I try with wvdial command (this wvdial will not finish until we stop it), I need a thread to read the output of wvdial
Actually, the problem is that Process.getInputStream() returns a BufferedReader.
So, even if the called subprocess flushes all its output, a read in the calling Java program will only get it if the buffer is full.

How to edit file on console from Java?

I'm trying to edit a file from CLI. I'm executing the nano command (I know that command will always be available); when I execute it, I can see nano's output but I cannot interact with it. How can I pass user input to the command? Do you have a better idea to easily edit a file from within my Java app?
This is my code:
String command = "nano /tmp/163377867.txt ";
try {
Process process = Runtime.getRuntime().exec(command);
InputStream inputStream = process.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
The problem with Java's Runtime.exec is that it connects stdin and stdout to "pipes," while many console programs need a TTY device.
One way to solve this problem is to make the Java program's controlling terminal available to the program you execute:
Process proc = Runtime.getRuntime().exec(new String[]{
"sh", "-c", command+" </dev/tty >/dev/tty"});
proc.waitFor(); // wait for user to finish editing the file

Categories

Resources