读取输出时如何处理Java Processs的孙代?

时间:2018-07-26 10:04:53

标签: java multithreading process stream

使用命令行的JavaFX样机时,我遇到了以下问题:
如果我运行一个进程(例如批处理文件),而该进程又运行另一个进程(例如用简单的width: calc((99% - 2 * #{$gutter-horizontal}) / 3);打开记事本),我似乎无法正确确定批处理文件何时完成执行:

  • Process#waitFor在批处理文件启动时已经返回(我想是因为我必须在可执行文件之前添加start notepad,而cmd确实在一秒钟内就完成了)
  • 仅在我关闭记事本之后(而不是在批处理文件终止之后)使用Process#getInputStream读取输出。

我是否一直缺少一种方法?而且更重要的是:如何确定cmd /c产生的过程的结束?

可复制的示例:

example.bat:

cmd /c

JavaCode:

@echo off
start notepad
REM You can change the path to something else but it should be something where tree produces a longer output to reproduce the problem.
cd %USERPROFILE%\Desktop
tree

开始打印树,并且在中间打印“处理结束”被写入控制台。最后,在我关闭记事本窗口后,打印出“阅读结束”。
我的目标是忽略记事本的关闭时间,找到树已完成打印(即批处理文件已完成处理)的位置。

基于Leviands's answer,我尝试在完成执行后无济于事地关闭“流程”流。
不幸的是,InputStream再次关闭了中间内容,ErrorStream(我也在实际应用程序中阅读过)也没有关闭,因此阻塞了线程。

2 个答案:

答案 0 :(得分:2)

首先,非常感谢Leviand在他们的InputStream#available中提到answer,这使我得到了一些切实可行的东西:

这个想法是,在我正在寻找的时间点上,Process#isAlive应该返回false,因为Stream需要比Process进程更长的时间来处理(如果有意义),而InputStream不应读取任何字符。 ,因此InputStream#available应该返回0。

这导致这段代码:

import java.io.IOException;
import java.io.InputStream;

public class Main {

    public static void main(String[] args) {
        Process myProcess = null;
        try {
            myProcess = Runtime.getRuntime().exec("cmd /c C:\\Users\\geisterfurz007\\Desktop\\example.bat");
        } catch (IOException e) {
            e.printStackTrace();
        }
        startReadingThread(myProcess).start();
    }

    private static Thread startReadingThread(Process myProcess) {
        InputStream stream = myProcess.getInputStream();
        return new Thread(() -> {
            int character;
            try {
                while (myProcess.isAlive() || stream.available() > 0) {

                    if ((character = stream.read()) == -1) {
                        break;
                    }
                    System.out.write(character);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

}

使用上面的代码,我可以忽略所有孙进程而从Processes流中进行读取。


一段时间后,有几件事要解决:

  • 我现在移至使用InputStreamReader的主要原因是:我可以指定编码。然后,所有出现的stream.available() > 0必须替换为reader.ready()
  • 闲置时会消耗大量资源!如果没有任何要读取的内容,则在尝试再次读取之前,让线程休眠数毫秒是很有意义的。
  • 至少在我将每个字符逐个发送到GUI的用例中,对于较长的输出,这会很快杀死GUI。在对主线程进行进一步处理之前,请考虑某种类型的输出缓冲区。

答案 1 :(得分:1)

您要启动startReadingThread(myProcess);,然后用myProcess.waitFor();告诉进程myProcess结束,然后再打印System.out.println("Reading ended");,这与您愿意执行的相反

应该“阻止”另一个人开始的过程是startReadingThread

该问题也出现在while循环中,这是不正确的。 我会做出类似的更改(不确定stream.available() != 0,但它正在测试中):

    private static Thread startReadingThread(Process myProcess) {
    InputStream stream = myProcess.getInputStream();
    return new Thread(() -> {
        int character;
        try {
            while (stream.available() != 0) {
                if((character = stream.read()) == -1) {
                    System.out.write(character);
                    stream.close();
                    break;
                }
                System.out.write(character);
            }
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                System.out.println("Reading ended");
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
}

然后使用以下命令编辑主文件:

public static void main(String[] args) {
    Process myProcess = null;
    try {
        myProcess = Runtime.getRuntime().exec("cmd /c C:\\Users\\geisterfurz007\\Desktop\\example.bat");
           } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        if(myProcess.waitFor() == 0){
            startReadingThread(myProcess).start();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Process ended");

输出(我来自意大利)

  

已连接到目标VM,地址:“ 127.0.0.1:56607”,传输:   'socket'过程已结束   卷OSDisk Numero序列号:12DA-8173 C:。非抗精神病药   sottocartelle

     

阅读结束:已与目标VM断开连接,地址:   '127.0.0.1:56607',运输工具:'socket'

     

以退出代码0结束的过程

编辑: 这是对里面有子文件夹的文件夹的测试(我在屏幕快照中附加了奇怪符号的原因)

test result

这是文件夹内容:

folder content