![](/img/trans.png)
[英]How can I read and write a process in real time and display output in JTextArea in real time? (Java Swing)
[英]Get process output in real time with java
我想在 java 中打印一个进程的实时 output; 但是 output (几乎)仅在进程停止执行时才可用。
换句话说,当我使用命令行运行相同的进程时,我得到的反馈(输出)比使用 Java 执行时更频繁。
我测试了以下代码以每 250 毫秒打印一次 output:
private static String printStream(BufferedReader bufferedReader) {
try {
String line = bufferedReader.readLine();
if(line != null) {
System.out.println(line);
}
return line;
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
public static void sendProcessStreams(Process process, SendMessage sendMessage) {
String[] command = { "/bin/bash", "-c", "custom_process"};
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.directory(new File("."));
Process process = processBuilder.start();
BufferedReader inputBufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()), 1);
BufferedReader errorBufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()), 1);
System.out.println("Start reading process");
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
String inputLine = printStream(inputBufferedReader);
String errorLine = printStream(errorBufferedReader);
if(inputLine == null && errorLine == null && !process.isAlive()) {
timer.cancel();
}
}
}, 0, 250);
}
然而它只打印两行,然后等待进程结束,然后再打印其他所有内容。
是否有可能从外部流程获得更频繁的反馈? 如果不是,为什么不呢?
您的代码基本上(就从输出中读取而言)工作正常,问题肯定是出于另一个原因,请构建一个可重现的最小问题(例如,什么是custom_process
,为什么在可以使用当前线程时使用Timer
, ...)。
无论如何,这是一个实时读取 output 的示例:
final Process proc = new ProcessBuilder(
"/bin/bash", "-c",
"for i in `seq 1 10`; do echo $i; sleep $((i % 2)); done")
.start();
try(InputStreamReader isr = new InputStreamReader(proc.getInputStream())) {
int c;
while((c = isr.read()) >= 0) {
System.out.print((char) c);
System.out.flush();
}
}
使用 output:
1 (and wait one second)
2
3 (and wait one second)
4
5 (and wait one second)
6
7 (and wait one second)
8
9 (and wait one second)
10
似乎您正在处理多线程问题,而不是获取进程的 output。
我刚刚做了这个演示 class 你可以使用:
CommandExecTest.java
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
public class CommandExecTest {
public static void main(String[] args) throws InterruptedException {
String executable = "cmd";
String[] commandParams = {"@ping -n 5 localhost","echo \"hello world\"","exit 123"};
boolean passCommandsAsLinesToShellExecutableAfterStartup = true;
AsyncExecutor asyncExecutor = new AsyncExecutor(executable, commandParams,passCommandsAsLinesToShellExecutableAfterStartup);
System.out.println("x"+"/x\tsecs in main thread \t\t status:"+asyncExecutor.runstate+" of async thread that monitors the process");
asyncExecutor.start();//start() invokes the run() method as a detached thread
for(int i=0;i<10;i++) {
// you can do whatever here and the other process is still running and printing its output inside detached thread
Thread.sleep(1000);
System.out.println(i+"/10\tsecs in main thread \t\t status:"+asyncExecutor.runstate+" of async thread that monitors the process");
}
asyncExecutor.join(); // main thread has nothing to do anymore, wait till other thread that monitor other process finishes as well
System.out.println("END OWN-PROGRAMM: 0 , END OTHER PROCESS:"+asyncExecutor.processExitcode);
System.exit(0);
}
}
运行状态.java
public static enum Runstate {
CREATED, RUNNING, STOPPED
}
AsyncExecutor.java
public static class AsyncExecutor extends Thread{
private String executable;
private String[] commandParams;
public ArrayList<String> linesSoFarStdout = new ArrayList<>();
public ArrayList<String> linesSoFarStderr = new ArrayList<>();
public Runstate runstate;
public int processExitcode=-1;
private boolean passCommandsAsLinesToShellExecutableAfterStartup = false;
public AsyncExecutor(String executable, String[] commandParams) {
this.executable=executable;
this.commandParams=commandParams;
this.runstate=Runstate.CREATED;
this.passCommandsAsLinesToShellExecutableAfterStartup=false;
}
/**
* if you want to run a single-process with arguments use <b>false</b> example executable="java" commandParams={"-jar","myjarfile.jar","arg0","arg1"}
* <p>
* if you want to run a shell-process and enter commands afterwards use <b>true</b> example executable="cmd" commandParams={"@ping -n 5 localhost","echo \"hello world\"","exit 123"}
* @param executable
* @param commandParams
* @param passCommandsAsLinesToShellExecutableAfterStartup
*/
public AsyncExecutor(String executable, String[] commandParams, boolean passCommandsAsLinesToShellExecutableAfterStartup) {
this.executable=executable;
this.commandParams=commandParams;
this.runstate=Runstate.CREATED;
this.passCommandsAsLinesToShellExecutableAfterStartup=passCommandsAsLinesToShellExecutableAfterStartup;
}
@Override
public void run() {
this.runstate=Runstate.RUNNING;
// 1 start the process
Process p = null;
try {
if(passCommandsAsLinesToShellExecutableAfterStartup) {
// open a shell-like process like cmd and pass the arguments/command after opening it
// * example:
// * open 'cmd' (shell)
// * write 'echo "hello world"' and press enter
p = Runtime.getRuntime().exec(new String[] {executable});
PrintWriter stdin = new PrintWriter( p.getOutputStream());
for( int i = 0; i < commandParams.length; i++) {
String commandstring = commandParams[i];
stdin.println( commandstring);
}
stdin.close();
}
else {
// pass the arguments directly during startup to the process
// * example:
// * run 'java -jar myexecutable.jar arg0 arg1 ...'
String[] execWithArgs = new String[commandParams.length+1];
execWithArgs[0] = executable;
for(int i=1;i<=commandParams.length;i++) {
execWithArgs[i]=commandParams[i-1];
}
p = Runtime.getRuntime().exec( execWithArgs);
}
// 2 print the output
InputStream is = p.getInputStream();
BufferedReader br = new BufferedReader( new InputStreamReader( is));
InputStream eis = p.getErrorStream();
BufferedReader ebr = new BufferedReader( new InputStreamReader( eis));
String lineStdout=null;
String lineStderr=null;
while(p.isAlive()) {
Thread.yield(); // *
// * free cpu clock for other tasks on your PC! maybe even add thread.sleep(milliseconds) to free some more
// * everytime this thread gets cpu clock it will try the following codeblock inside the while and yield afterwards for the next time it gets cpu-time from sheduler
while( (lineStdout = br.readLine()) != null || (lineStderr = ebr.readLine()) != null) {
if(lineStdout!=null) {
System.out.println(lineStdout);
linesSoFarStdout.add(lineStdout);
}
if(lineStderr!=null) {
System.out.println(lineStderr);
linesSoFarStderr.add(lineStderr);
}
}
}
// 3 when process ends
this.processExitcode = p.exitValue();
}
catch(Exception e) {
System.err.println("Something went wrong!");
e.printStackTrace();
}
if(processExitcode!=0) {
System.err.println("The other process stopped with unexpected existcode: " + processExitcode);
}
this.runstate=Runstate.STOPPED;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.