[英]Java process swing InputStream and OutputStream operations
我有slcanterm.exe
命令行實用程序,該實用程序通過連接到PC的USB-CAN適配器執行終端功能。 我需要從Java應用程序啟動此實用程序,然后在循環中讀寫此終端。 我的問題是我不能讓InputStream和OutputStream一起工作:
System.out.println( "hello" );
String line;
OutputStream stdin = null;
InputStream stderr = null;
InputStream stdout = null;
Process process = Runtime.getRuntime ().exec ("cmd.exe");
stdin = process.getOutputStream ();
stderr = process.getErrorStream ();
stdout = process.getInputStream ();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
BufferedReader reader = new BufferedReader (new InputStreamReader (stdout));
line = "slcanterm.exe" + "\n";
writer.write(line );
writer.flush();
// If I uncomment this line reader.readLine() loop is stucked
//writer.close();
while ((line = reader.readLine ()) != null)
System.out.println ("[Stdout] " + line);
System.out.println( "buy" );
如果我不關閉writer
流的while ((line = reader.readLine ()) != null)
留stucked。 我需要執行以reader.readLine ()
工作reader.readLine ()
和writer.write(line )
在一起。 有什么解決辦法嗎?
1)您的代碼(基本上)是正確的。 但是,我在另一個過程中遇到了類似的問題,該過程在關閉其輸入流時反應不佳。 如果關閉進程的輸入流(編寫器),則該進程實際上會發現其輸入流已關閉,並且在嘗試從中讀取時失敗。 就我而言,當他的讀取失敗時,我開始的過程沒有很好地響應。 這似乎是相同的問題。
2)您可以通過直接啟動slcanterm.exe而不使用cmd.exe來避免上述情況。
3)我看到的另一種可能性是,您僅附加了一個“ \\ n”,也許cmd.exe需要一個“ \\ n \\ r”。
輸出流將保持打開狀態,直到進程退出。 例如,當進程決定終止時,該進程退出,該進程被明確關閉,或通過關閉輸入流來隱式關閉。
這意味着您的readLine
將一直阻塞,直到cmd.exe
有更多輸出為止,但是由於您沒有發送更多命令,因此沒有任何命令。 而且永遠不會。
在簡單的情況下,您可以關閉輸入或寫exit
命令以關閉控制台。 如果slcanterm.exe
是您要運行的唯一程序,則可以直接啟動該過程。 如果要運行更多命令,則需要一種方法來檢測錯誤(不要忽略錯誤流)或檢測命令的輸出已終止。 如果您運行的命令沒有一致的最后一行,那么您可以將echo ---MARKER---
寫到stdin
並檢測該行何時出現(這意味着最后一條命令已終止,並且已執行了echo
)。
許多可能性。 這取決於您想做什么。
下面的課程演示了我所建議的簡單技術。 此外,它確保echo off
並且所有非命令輸出都將被忽略(因為cmd.exe
輸出每個輸入字符)。
class CommandRunner implements Closeable {
private Process cmd;
private OutputStreamWriter stdin;
private BufferedReader stderr;
private BufferedReader stdout;
private static class ArtificialOutput {
public List<String> inputLines;
public String finalMarker;
public ArtificialOutput(List<String> inputLines, String finalMarker) {
super();
this.inputLines = inputLines;
this.finalMarker = finalMarker;
}
}
public CommandRunner() throws IOException {
cmd = Runtime.getRuntime().exec("cmd.exe");
stdin = new OutputStreamWriter(cmd.getOutputStream());
stderr = new BufferedReader(new InputStreamReader(cmd.getErrorStream()));
stdout = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
// turn off echo of commands (also skips any console header lines)
execute("echo off", StdinWriter.WRITE_NOTHING, StdoutProcessor.IGNORE);
}
@Override
public void close() throws IOException {
stdin.close();
stdout.close();
stderr.close();
cmd.destroy();
}
public void execute(String command, StdinWriter writer, StdoutProcessor processor) throws IOException {
processOutput(executeCommand(command, writer), processor);
}
private ArtificialOutput executeCommand(String command, StdinWriter writer) throws IOException {
List<String> lines = new ArrayList<String>();
// add command as input line
lines.add(command);
// add all custom input lines
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (OutputStreamWriter bufferWriter = new OutputStreamWriter(buffer)) {
writer.write(bufferWriter);
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buffer.toByteArray())))) {
String line = null;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
// add stdout and sterr markers as input lines
String marker = "--- MARKER " + System.currentTimeMillis() + Math.random();
lines.add("echo " + marker + ">&2");
lines.add("echo " + marker);
// write everything to stdin
for (String line : lines) {
stdin.write(line + System.lineSeparator());
}
stdin.flush();
// our artificial outputs (with echo off) are the input lines and the closing marker
return new ArtificialOutput(lines, marker);
}
private void processOutput(ArtificialOutput artificialOutput, StdoutProcessor processor) throws IOException {
byte[] output = readUntilMarker(artificialOutput, stdout);
byte[] errors = readUntilMarker(artificialOutput, stderr);
if (errors.length > 0) {
processor.processError(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(errors))));
}
processor.processOuput(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(output))));
}
private byte[] readUntilMarker(ArtificialOutput artificialOutput, BufferedReader reader) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(buffer))) {;
String line;
while ((line = reader.readLine()) != null) {
// ignore input lines because cmd.exe outputs every input character
if (artificialOutput.inputLines.remove(line)) {
continue;
}
// the final marker indicates the command has ended
if (line.equals(artificialOutput.finalMarker)) {
break;
}
// everything else is the command output
writer.write(line + System.lineSeparator());
}
}
return buffer.toByteArray();
}
}
有了此類,您可以在程序中執行以下操作:
try (CommandRunner cmd = new CommandRunner()) {
cmd.execute("dir", StdinWriter.WRITE_NOTHING, StdoutProcessor.PRINT);
cmd.execute("echo something", StdinWriter.WRITE_NOTHING, StdoutProcessor.PRINT);
}
如果您只對slcanterm.exe的輸出感興趣,則可以讀取stdin OutputStream直到關閉它(即程序停止)。
關閉程序的stdin,stdout或stderr會使它在大多數時間終止(因為這是大多數shell的標准行為),因此您要避免對它們調用close()。
如果您要連續執行多個程序,建議您編寫一個具有適當錯誤處理和輸出過濾的批處理文件,然后使用Java執行該文件(或者甚至可以逐個執行它們,以啟動多個進程)。 遍歷cmd.exe幾乎永遠不值得多次處理輸入命令並等待響應的麻煩(而且使用cmd.exe還會失去很多可移植性)。
這么說,您的readLine()調用被卡住的原因是,stdout上沒有任何內容。 我敢打賭,cmd.exe需要Windows換行符(“ \\ n \\ r”)才能執行命令,或者slcanterm.exe不輸出任何內容(或者可能只是輸出到stderr?)。
System.out.println( "hello" );
String line;
Process process = Runtime.getRuntime ().exec ("cmd.exe /c slcanterm.exe");
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedReader error = new BufferedReader (new InputStreamReader(stderr));
new Thread() {
public void run() {
try {
String err;
while ((err = error.readLine ()) != null)
System.out.println ("[Stderr] " + err);
} catch (Exception e) {
e.printStackTrace();
}
}}.start();
while ((line = reader.readLine ()) != null)
System.out.println ("[Stdout] " + line);
System.out.println( "buy" );
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.