当前位置: 首页 > 知识库问答 >
问题:

Java -连续使用ProcessBuilder的输入和输出流

虞正业
2023-03-14

我想在提取一些数据时使用外部工具(循环通过行)。为此,我首先使用了Runtime.getRuntime()。exec()执行它。但后来我的提取变得很慢。所以我在寻找一种可能性,在循环的每个实例中,使用shell的同一个实例来执行外部工具。

我发现,我应该使用ProcessBuilder。但是现在还不行。

这是我测试执行的代码(已经从论坛中的答案输入):

public class ExecuteShell {
   ProcessBuilder builder;
   Process process = null;
   BufferedWriter process_stdin;
   BufferedReader reader, errReader;

   public ExecuteShell() {
    String command;
    command = getShellCommandForOperatingSystem();
    if(command.equals("")) {
        return; //Fehler!  No error handling yet
    }
    //init shell
    builder = new ProcessBuilder( command);
    builder.redirectErrorStream(true);
    try {
        process = builder.start();
    } catch (IOException e) {
        System.out.println(e);
    }

    //get stdout of shell  
    reader    = new BufferedReader(new InputStreamReader(process.getInputStream()));  
    errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

    //get stdin of shell
    process_stdin = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
    System.out.println("ExecuteShell: Constructor successfully finished");
   }

   public String executeCommand(String commands) {
    StringBuffer output;
    String line;
    try {
        //single execution
        process_stdin.write(commands);
        process_stdin.newLine();
        process_stdin.flush();
    } catch (IOException e) {
        System.out.println(e);
    }
    output    = new StringBuffer();
    line = ""; 

    try {
        if (!reader.ready()) {
            output.append("Reader empty \n");
            return output.toString();
        }
        while ((line = reader.readLine())!= null) {
            output.append(line + "\n");
            return output.toString();
        }
        if (!reader.ready()) {
            output.append("errReader empty \n");
            return output.toString();
        }
        while ((line = errReader.readLine())!= null) {
            output.append(line + "\n");
        }
    } catch (Exception e) {
        System.out.println("ExecuteShell: error in executeShell2File");
        e.printStackTrace();
        return "";
    }
    return output.toString();
   }


   public int close() {
    // finally close the shell by execution exit command
    try {
        process_stdin.write("exit");
        process_stdin.newLine();
        process_stdin.flush();
    }
    catch (IOException e) {
        System.out.println(e);
        return 1;
    }
    return 0;
   }

   private static String getShellCommandForOperatingSystem() {
    Properties prop = System.getProperties( );
    String os =  prop.getProperty( "os.name" );
    if ( os.startsWith("Windows") ) {
        //System.out.println("WINDOWS!");
        return "C:/cygwin64/bin/bash";
    } else if (os.startsWith("Linux") ) { 
        //System.out.println("Linux!");
        return"/bin/sh";
    }
    return "";      
   }
}

我想在另一个类中调用它,例如 Testclass:

public class TestExec{
    public static void main(String[] args) {
        String result = "";
        ExecuteShell es = new ExecuteShell();
        for (int i=0; i<5; i++) {
          // do something
          result = es.executeCommand("date"); //execute some command
          System.out.println("result:\n" + result); //do something with result
          // do something
        }
        es.close();
    }
}

我的问题是,输出流始终为空:

ExecuteShell: Constructor successfully finished
result:
Reader empty 

result:
Reader empty 

result:
Reader empty 

result:
Reader empty 

result:
Reader empty 

我在这里阅读了线程: 具有输入/输出流的 Java 进程

但是这些代码片段还不足以让我走,我错过了一些东西。我并没有真正与不同的线程进行过太多的工作。我也不确定扫描仪是否对我有帮助。我真的很感谢你的帮助。

最终,我的目标是重复调用外部命令并使其快速。

编辑:我改变了循环,所以es。close()在外面。我想补充一点,我不想只在循环中使用这个。

编辑:时间的问题是,我调用的命令导致了一个错误。当命令不会导致错误时,时间是可以接受的。

谢谢大家的回答

共有1个答案

井昊乾
2023-03-14

您可能正在经历一个竞争条件:在向shell写入命令后,您的Java程序继续运行,并且几乎立即调用reader.ready()。您想要执行的命令可能还没有输出任何东西,因此阅读器没有可用的数据。另一种解释是该命令不向stdout写入任何东西,而只向stderr写入任何东西(或者shell,也许它无法启动该命令?)。然而,您实际上并没有从stderr读取。

要正确处理输出和错误流,您不能检查reader.ready(),但需要在循环中调用readLine()(等待数据可用)。使用您的代码,即使程序会到达那个点,您也只会从输出中读取一行。如果程序输出多行,这些数据将被解释为下一个命令的输出。典型的解决方案是在循环中读取,直到readLine()返回null,但这在这里不起作用,因为这意味着您的程序将在此循环中等待,直到shell终止(这永远不会发生,所以它只会无限挂起)。如果您不知道每个命令将写入stdout和stderr多少行,则修复此问题几乎是不可能的。

但是,使用 shell 并向其发送命令的复杂方法可能是完全没有必要的。从 Java 程序内部和 shell 中启动命令同样快,而且编写起来更容易。同样,Runtime.exec() 和 ProcessBuilder 之间没有性能差异(前者只是调用后者),只有在需要其高级功能时才需要 ProcessBuilder

如果您在调用外部程序时遇到性能问题,您应该找出它们的确切位置并尝试解决它们,但不是用这种方法。例如,通常启动一个线程来读取输出和错误流(如果您不启动单独的线程并且命令产生大量输出,一切都可能挂起)。这可能很慢,因此您可以使用线程池来避免进程的重复生成。

 类似资料:
  • 我想用java代码调用一个外部程序,然后Google告诉我Runtime或ProcessBuilder可以帮助我完成这项工作。我试过了,结果发现java程序无法退出,这意味着子进程和父进程都将永远等待。它们要么挂起,要么陷入僵局。 有人告诉我原因是子进程的缓存太小了。当它试图将数据返回给父进程时,但是父进程没有及时读取它,然后他们两个都挂起了。所以他们建议我叉一个线程来负责读取子进程的缓存数据。我

  • 问题内容: 对于Java练习,我试图创建一个程序,该程序从键盘读取整数,直到输入负数为止。 并打印整数的最大值和最小值,而忽略负数。 一旦运行,是否可以在同一程序中进行连续输入?我必须每次都继续运行该程序以输入数字。 任何帮助,将不胜感激 这是我的代码的一部分-它可以工作,但是我认为有一种更简单的方法可以执行我想要的操作,但不确定如何操作! 问题答案:

  • 在使用ProcessBuilder的Java应用程序中,我无法读取在git bash中运行的git命令的输出。 OS:Windows 8.1---IDE:IntelliJ 使用processBuilder打开git bash并在其中执行命令 但是,我仍然不明白为什么不能用ProcessBuilder读取输出

  • 本小节将会介绍基本输入输出的 Java 标准类,通过本小节的学习,你将了解到什么是输入和输入,什么是流;输入输出流的应用场景,File类的使用,什么是文件,Java 提供的输入输出流相关 API 等内容。 1. 什么是输入和输出(I / O) 1.1 基本概念 输入/输出这个概念,对于计算机相关专业的同学并不陌生,在计算中,输入/输出(Input / Output,缩写为 I / O)是信息处理系

  • 输出 用print加上字符串,就可以向屏幕上输出指定的文字。比如输出'hello, world',用代码实现如下: >>> print 'hello, world' print语句也可以跟上多个字符串,用逗号“,”隔开,就可以连成一串输出: >>> print 'The quick brown fox', 'jumps over', 'the lazy dog' The quick brown

  • 输出 用print()在括号中加上字符串,就可以向屏幕上输出指定的文字。比如输出'hello, world',用代码实现如下: >>> print('hello, world') print()函数也可以接受多个字符串,用逗号“,”隔开,就可以连成一串输出: >>> print('The quick brown fox', 'jumps over', 'the lazy dog') The qu