在Windows上执行控制台命令和实时流输出非常慢

时间:2019-06-24 19:58:19

标签: java windows command-line

我花了几天时间寻找一种实时读取控制台应用程序输出的方法。我遇到的每一种方式几乎都与其他方式完全相同,但是似乎不起作用。每当我运行代码时,我的控制台命令都不会在几分钟内输出任何文本,这使我认为它实际上是在执行结束时而不是在执行期间输出数据。但是,现在我知道存在某种瓶颈,我不认为这与我的代码有关。

很难交出一个更简单的可重现示例,因为我认为这会使理解变得更加困难,所以请原谅。我什至不确定这个问题是否可以重现,因为这似乎是环境问题。

我正在运行Windows 7(我知道,我很酷)。

我正在使用IntelliJ IDEA IDE(它几乎超过了运行Windows 7的感觉)。

这是java --version的输出:

  

Java 11.0.3 2019-04-16 LTS

     

Java(TM)SE运行时环境18.9(内部版本11.0.3 + 12-LTS)

     

Java HotSpot(TM)64位服务器VM 18.9(内部版本11.0.3 + 12-LTS,混合模式)

为说明问题,我有以下代码:

public class Main {
  private static Timer timer;

  public static void main(String[] args) throws IOException {
    java.lang.String[] commands = {"cmd", "/c", "sfc", "/scannow"};
    ProcessBuilder pBuilder = new ProcessBuilder(commands);
    pBuilder.redirectErrorStream();

    timer = new Timer();

    try {
      dbg("Starting process...");
      Process p = pBuilder.start();
      dbg("Process started.");

      InputStream in = p.getInputStream();
      final Scanner scanner = new Scanner(in);

      new Thread(() -> {
        dbg("Thread started.");
        int i = 1;
        while (scanner.hasNextLine()) {
          String nextLine = scanner.nextLine();
          dbg("Line " + i + " - " + nextLine);

          i++;
        }
        scanner.close();

        dbg("Closing scanner.");
      }).start();

      dbg("Waiting for exit status result...");
      int exitStatus = p.waitFor();
      dbg("exit status: " + exitStatus);
      p.destroy();
    } catch (NullPointerException | InterruptedException | IOException e) {
      e.printStackTrace();
    }
  }

  private static void dbg(String messageToAppend) {
    System.out.println(timer.getTimePassedAsStr(messageToAppend));
  }
}

我制作了一个简单的计时器类来帮助我调试代码,因为开始运行花费的时间比我预期的要长得多。它工作正常,所以我不会发布代码。但是,这是我得到的运行结果:

[000:00.000] Starting process...
[000:00.149] Process started.
[000:00.150] Input stream created.
[000:00.204] Scanner created.
[000:00.205] Waiting for exit status result...
[000:00.205] Thread started.
[005:51.762] Line 1 - 
[005:51.763] Line 2 - 
[005:51.763] Line 3 - Beginning system scan.  This process will take some time.
[005:51.763] Line 4 - 
[005:51.763] Line 5 - 
[005:51.764] Line 6 - 
[005:51.764] Line 7 - Beginning verification phase of system scan.
[005:51.764] Line 8 - 
[012:42.484] Line 9 - Verification 100% complete.
[012:42.485] Line 10 - Windows Resource Protection found corrupt files but was unable to fix some of them.
[012:42.486] Line 11 - 
[012:42.486] Line 12 - Details are included in the CBS.Log windir\Logs\CBS\CBS.log. For example 
[012:42.487] Line 13 - 
[012:42.487] Line 14 - C:\Windows\Logs\CBS\CBS.log
[012:42.487] Line 15 - 
[012:42.488] Closing scanner.
[012:42.491] exit status: 0

通常,当我直接从控制台运行sfc /scannow时,它几乎立即启动,但是当我从Java应用程序中运行它时,几乎有6分钟的延迟才能得到任何输出。

我也试图像这样通过CMD解释器执行它:

java.lang.String[] commands = {"cmd", "/c", "sfc", "/scannow"};

但是我得到相同的结果。我还尝试过将其作为单线程运行,如下所示:

public static void runSfc() throws IOException {
  timer = new Timer();

  Runtime rt = Runtime.getRuntime();
  String[] commands = {"sfc", "/scannow"};

  dbg("Running command...");
  Process proc = rt.exec(commands);
  BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
  BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

  String s;
  int i = 1;

  dbg("Here is the standard output of the command:");
  while ((s = stdInput.readLine()) != null) {
    dbg("Line " + i + " - " + s);
    i++;
  }

  i = 1;
  dbg("Here is the standard error of the command (if any):");
  while ((s = stdError.readLine()) != null) {
    dbg("Line " + i + " - " + s);
    i++;
  }
}

似乎随后的每个运行(无论是单线程还是多线程)花费的时间大致相同,甚至更长,甚至莫名其妙地随后又缩短了。最近一次运行是在控制台窗口中看到第一行输出之前的15分钟。

该应用需要以管理员身份运行吗?

我是从IDE运行代码,还是从命令行编译并运行代码都没关系。

我尝试重新安装Java-没什么区别。

我尝试使用版本吗?相反-没有区别。

奇怪的是,当扫描因为已经在其他地方运行而无法运行时,它在大约25秒(超过5分钟的时间)后开始其初始输出。在线程内运行时也是如此。

[000:00.000] Running command...
[000:00.203] Here is the standard output of the command:
[000:25.508] Line 1 - 
[000:25.509] Line 2 - 
[000:25.509] Line 3 - Beginning system scan.  This process will take some time.
[000:25.509] Line 4 - 
[000:25.509] Line 5 - 
[000:25.509] Line 6 - Another servicing or repair operation is currently running.  
[000:25.509] Line 7 - 
[000:25.509] Line 8 - Wait for this to finish and run sfc again.
[000:25.509] Line 9 - 
[000:25.510] Here is the standard error of the command (if any):
[000:25.510] End of execution

问题1:为什么从命令行运行该程序时,输出任何东西要花5分钟以上的时间,几乎立即产生输出? Windows机器上的其他任何人都会遇到这种情况吗?

问题2:我想在扫描进行过程中收到进度更新。我还没有意识到,由于进度更新之前带有\ r且在过程达到100%之前不会以\ n结尾,因此在扫描完成之前,我看不到任何更新消息,因为它从未实际注册为已接收到一行输出。显然,我需要读取字节而不是行。没有人知道一种阅读这种文本的方式(以\ r开头,直到过程结束时才以\ n结尾)而无需我重新发明轮子的方法吗?

编辑: 直接通过CLI执行时的平均平均执行时间 13m 53s(5次跑步)

通过Java应用执行时的平均执行时间 12m 32s(5次跑步)

因此,看起来Java应用程序内的执行时间与直接在控制台中执行的时间大致相同,并且执行时间一点也不慢。只是需要一段时间才能显示输出的第一行。

我只能得出结论,这确实是@Andreas建议的缓冲区刷新问题。就目前而言,我认为没有办法解决这个问题。

0 个答案:

没有答案