Java 读取进程输出,无缓冲/实时

标签 java process progress-bar stdout

我想在进程生成时读取它的标准输出。 该过程将发送进度指示器的信息,因此我一次获取所有信息是没有意义的,我这样做了,这就是问题所在。我尝试按照帖子中的建议使用 Scanner 类,但只有在该过程完成后我仍然得到输出。 我意识到这个问题以前曾被问过,但尚未得到解答。

您可能需要先查看 StreamGobblerOutput 类。

    public List<String> executeCall(String fileName) 
    {
    StringBuilder sbOutput = new StringBuilder();
    StringBuilder sbError = new StringBuilder();

    File file = new File(fileName);
    try ( BufferedReader br = new BufferedReader(new FileReader(file)) ) {
        String line;
        while ((line = br.readLine()) != null) {
           String [] parts = line.split("\\s");
           if(parts.length<2) {
               sbError.append("Command too short for call: " + parts[0]);
               continue;
           }

            List<String> args = new ArrayList<String>();
            args.add ("sfb.exe");
            for(int i = 1; i <parts.length; ++i) {
                args.add (parts[i]);
            }
            args.add (sfbPassword);

            ProcessBuilder pb = new ProcessBuilder (args);
            pb.directory(new File(Support.getJustThePathFromFile(file)));
            Map<String, String> envs = pb.environment();
            String path = envs.get("Path");
            envs.put("Path", Paths.get(".").toAbsolutePath().normalize().toString() + ";" +path);


            //pb.redirectOutput(new Redirect() {});
            Process p = pb.start();

            String outputPathPrefix = pb.directory().getCanonicalPath();

            // any output?
            StreamGobblerOutput outputGobbler = new StreamGobblerOutput(p.getInputStream(), outputPathPrefix);
            outputGobbler.start();

            // any errors?
            StreamGobblerError errorGobbler = new StreamGobblerError(p.getErrorStream());
            errorGobbler.start();


            try
            {
                p.waitFor();
            }
            catch (InterruptedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            sbOutput = outputGobbler.getOutput();
            sbError = errorGobbler.getErrors();

            String rootPath= Support.getJustThePathFromFile(new File(fileName));
            File rootFile = new File(rootPath + "/..");
            String rootFolder = rootFile.getCanonicalFile().getName();
            System.err.println("rootFolder: " + rootFolder);
            mainApp.addModifiedFiles(outputGobbler.getModifiedFileNames(), rootFolder);

        }
    } catch ( IOException ex) {
        sbError.append(ex.getMessage());
    }

    mainApp.addOutput(sbOutput.toString());
    mainApp.addError(sbError.toString());

    return;

}

    private class StreamGobblerOutput extends Thread {
        private InputStream is;
        private String outputPathPrefix;
        private StringBuilder sbOutput;
        private List<String> modifiedFileNames;
        private Scanner scanner;

        private StreamGobblerOutput(InputStream is, String outputPathPrefix) {
            this.is = is;
            this.outputPathPrefix = outputPathPrefix;
            sbOutput = new StringBuilder();
            modifiedFileNames = new ArrayList<String>();
            scanner = new Scanner(is);
        }

        public StringBuilder getOutput() {
            return sbOutput;    
        }

        public List<String> getModifiedFileNames() {
            return modifiedFileNames;       
        }

        @Override
        public void run() {
            //create pattern
            Pattern patternProgress = Pattern.compile("\\((\\d+)%\\)");

            //InputStreamReader isr = new InputStreamReader(is);
            //BufferedReader br = new BufferedReader(isr);
            String ligne = null;
            while (scanner.hasNextLine()) {
                ligne = scanner.nextLine();

                sbOutput.append(ligne);
                sbOutput.append("\r\n");
                //bw.write("\r\n");

                Matcher mProgress = patternProgress.matcher(ligne);
                if (mProgress.find()) {
                    int percentage = Integer.parseInt(mProgress.group(1));
                    System.err.println("percentage=" + percentage);
                    mainApp.mainWindowController.setProgressExecute(percentage/100.0);
                }
            }
            mainApp.mainWindowController.setProgressExecute(1.0);
            if (scanner != null) {
                scanner.close();
             }
        }
    }

    private class StreamGobblerError extends Thread {
        private InputStream is;
        private StringBuilder sbError;
        private Scanner scanner;

        private StreamGobblerError(InputStream is) {
            this.is = is;
            sbError = new StringBuilder();
            scanner = new Scanner(is);
        }

        public StringBuilder getErrors() {
            return sbError;     
        }

        @Override
        public void run() {
            //InputStreamReader isr = new InputStreamReader(is);
            //BufferedReader br = new BufferedReader(isr);
            String ligne = null;
            while (scanner.hasNextLine()) {
                ligne = scanner.nextLine();
                sbError.append(ligne);
                sbError.append("\r\n");
            }
            if (scanner != null) {
                scanner.close();
             }
        }
    }

更新:我尝试将输出重定向到文件并从中读取,但这似乎遇到了与之前的实现相同的缓冲问题:我只得到两个数据点。

作为解决方法,我必须要求 .exe 的创建者在显示进度的每行中包含 4100 个额外字符。

最佳答案

如果您的外部进程是基于 C/C++ (stdio) 的,则这很可能是 block 缓冲问题:

stdio-based programs as a rule are line buffered if they are running interactively in a terminal and block buffered when their stdout is redirected to a pipe. In the latter case, you won't see new lines until the buffer overflows or flushed.

参见this answer了解更多详细信息以及一些可能的解决方法。

另请注意,根据this ,行缓冲不是 Win32 上的一个选项:

_IOLBF For some systems, this provides line buffering. However, for Win32, the behavior is the same as _IOFBF - Full Buffering.

因此,如果您选择修改“exe”程序以使用 setvbuf 设置正确的输出模式,则必须使用:

_IONBF No buffer

相反。

关于Java 读取进程输出,无缓冲/实时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45075899/

相关文章:

c - 为什么这个全局计数器在子进程中不递减?

捕获程序输出

c# - 上传xml文件进度条

java - 如何在 NetBeans 中禁用 javadoc 拼写检查器

java - android 在新线程中运行类

java - 使用 MOXy 解码 LocalDate/LocalDateTime

java - 自定义 ListPreference 中的选定项目未更改

Linux:程序 A 运行程序 B 杀死 A,替换 A,并重新启动 A -- 继承打开文件描述符的问题

jquery - 如何在 Bootstrap 3 中制作进度条动画?

android - Android 中的动画进度条更新