簡體   English   中英

Java流程擺動InputStream和OutputStream操作

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM