78

我正在使用以下代码启动流程构建器。我想知道如何将其输出重定向到String.

ProcessBuilder pb = new ProcessBuilder(
    System.getProperty("user.dir") + "/src/generate_list.sh", filename);
Process p = pb.start();

我尝试使用ByteArrayOutputStream,但似乎没有用。

4

11 回答 11

92

InputStream. 您可以将输出附加到StringBuilder

BufferedReader reader = 
                new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
   builder.append(line);
   builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
于 2013-05-23T12:39:18.383 回答
29

使用 Apache Commons IOUtils,您可以在一行中完成:

ProcessBuilder pb = new ProcessBuilder("pwd");
String output = IOUtils.toString(pb.start().getInputStream(), StandardCharsets.UTF_8);
于 2016-05-25T13:16:57.190 回答
11

Java 8 示例:

public static String runCommandForOutput(List<String> params) {
    ProcessBuilder pb = new ProcessBuilder(params);
    Process p;
    String result = "";
    try {
        p = pb.start();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));

        StringJoiner sj = new StringJoiner(System.getProperty("line.separator"));
        reader.lines().iterator().forEachRemaining(sj::add);
        result = sj.toString();

        p.waitFor();
        p.destroy();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;
}

用法:

List<String> params = Arrays.asList("/bin/sh", "-c", "cat /proc/cpuinfo");
String result = runCommandForOutput(params);

我使用这个确切的代码,它适用于单行或多行结果。您也可以添加错误流处理程序。

于 2016-12-22T22:50:27.600 回答
9

你可能会做这样的事情:

private static BufferedReader getOutput(Process p) {
    return new BufferedReader(new InputStreamReader(p.getInputStream()));
}

private static BufferedReader getError(Process p) {
    return new BufferedReader(new InputStreamReader(p.getErrorStream()));
}
...
Process p = Runtime.getRuntime().exec(commande);
BufferedReader output = getOutput(p);
BufferedReader error = getError(p);
String ligne = "";

while ((ligne = output.readLine()) != null) {
    System.out.println(ligne);
}

while ((ligne = error.readLine()) != null) {
 System.out.println(ligne);
}
于 2013-05-23T12:40:50.447 回答
9

Java 9开始,我们终于有了一个内衬:

ProcessBuilder pb = new ProcessBuilder("pwd");
Process process = pb.start();

String result = new String(process.getInputStream().readAllBytes());
于 2019-10-06T17:16:04.250 回答
5

只需添加.inheritIO();到流程构建器行。

IE:

ProcessBuilder pb = new ProcessBuilder(script.sh).inheritIO();

于 2015-03-02T18:31:21.397 回答
5

对于 Java 7 和 8,这应该可以工作:

private String getInputAsString(InputStream is)
{
   try(java.util.Scanner s = new java.util.Scanner(is)) 
   { 
       return s.useDelimiter("\\A").hasNext() ? s.next() : ""; 
   }
}

然后在您的代码中,执行以下操作:

String stdOut = getInputAsString(p.getInputStream());
String stdErr = getInputAsString(p.getErrorStream());

我无耻地从:如何将 Process Builder 的输出重定向到字符串?

于 2016-01-19T00:12:18.387 回答
2

在尝试处理不同的情况后(同时处理 stderr 和 stdout 并且不阻塞任何这些,超时后终止进程,正确转义斜杠,引号,特殊字符,空格......)我放弃并找到了Apache Commons Exec https ://commons.apache.org/proper/commons-exec/tutorial.html似乎所有这些事情都做得很好。

我确实建议需要在 java 中调用外部进程的每个人都使用 Apache Commons Exec 库,而不是再次重新发明它。

于 2016-08-02T20:30:24.757 回答
2

在 java 8 中有一个不错的 lines() 流,您可以将其与 String.join 和 System.lineSeparator() 结合使用:

    try (BufferedReader outReader = new BufferedReader(new InputStreamReader(p.getInputStream()))
    {
        return String.join(System.lineSeparator(), outReader.lines().collect(toList()));
        \\ OR using jOOλ if you like reduced verbosity
        return Seq.seq(outReader.lines()).toString(System.lineSeparator())
    }
于 2016-05-17T13:52:48.390 回答
0

解决方案

  • 此代码是您问题的一般解决方案的运行示例:

如何将 Process Builder 的输出重定向到字符串?

  • Greg T 在尝试了多种解决方案来运行各种命令并捕获它们的输出之后,归功于 Greg T 的回答包含了特定解决方案的本质。我希望一般示例对在捕获输出的同时结合多个需求的人有用。
  • 要获得您的特定解决方案,您可以取消注释ProcessBuilder pb = new ProcessBuilder(System.getProperty("user.dir")+"/src/generate_list.sh", filename);、取消注释该行并注释掉: ProcessBuilder processBuilder = new ProcessBuilder(commands);.

功能性

  • 这是一个执行命令echo 1并将输出作为字符串返回的工作示例。
  • 我还添加了设置工作路径和环境变量,这对于您的特定示例不是必需的,因此您可以将其删除。

使用与验证

  • 您可以将此代码复制粘贴为一个类,将其编译为 jar 并运行它。
  • 它在 WSL Ubuntu 16.04 中得到验证。
  • 通过设置binaryCommand[0]="touch";binaryCommand[1]="1";,重新编译和运行.jar文件来验证设置工作目录。

限制

  • 如果管道已满(由于“太大”的输出),代码将挂起。

代码

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.StringJoiner;

public class GenerateOutput {

    /**
     * This code can execute a command and print the output accompanying that command.
     * compile this project into a .jar and run it with for example:
     * java -jar readOutputOfCommand.jar
     * 
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        boolean answerYes = false; // no yes answer to any command prompts is needed.

        // to execute a command with spaces in it in terminal, put them in an array of Strings.
        String[] binaryCommand = new String[2];

        // write a command that gives a binary output:
        binaryCommand[0] = "echo";
        binaryCommand[1] = "1";

        // pass the commands to a method that executes them
        System.out.println("The output of the echo command = "+executeCommands(binaryCommand,answerYes));
    }

    /**
     * This executes the commands in terminal. 
     * Additionally it sets an environment variable (not necessary for your particular solution)
     * Additionally it sets a working path (not necessary for your particular solution)
     * @param commandData
     * @param ansYes
     * @throws Exception 
     */
    public static String executeCommands(String[] commands,Boolean ansYes) throws Exception {
        String capturedCommandOutput = null;
        System.out.println("Incoming commandData = "+Arrays.deepToString(commands));
        File workingDirectory = new File("/mnt/c/testfolder b/");

        // create a ProcessBuilder to execute the commands in
        ProcessBuilder processBuilder = new ProcessBuilder(commands);
        //ProcessBuilder processBuilder = new ProcessBuilder(System.getProperty("user.dir")+"/src/generate_list.sh", "a");

        // this is not necessary but can be used to set an environment variable for the command
        processBuilder = setEnvironmentVariable(processBuilder); 

        // this is not necessary but can be used to set the working directory for the command
        processBuilder.directory(workingDirectory);

        // execute the actual commands
        try {

             Process process = processBuilder.start();

             // capture the output stream of the command
             BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringJoiner sj = new StringJoiner(System.getProperty("line.separator"));
            reader.lines().iterator().forEachRemaining(sj::add);
            capturedCommandOutput = sj.toString();
            System.out.println("The output of this command ="+ capturedCommandOutput);

             // here you connect the output of your command to any new input, e.g. if you get prompted for `yes`
             new Thread(new SyncPipe(process.getErrorStream(), System.err)).start();
             new Thread(new SyncPipe(process.getInputStream(), System.out)).start();
            PrintWriter stdin = new PrintWriter(process.getOutputStream());

            //This is not necessary but can be used to answer yes to being prompted
            if (ansYes) {
                System.out.println("WITH YES!");
            stdin.println("yes");
            }

            // write any other commands you want here

            stdin.close();

            // this lets you know whether the command execution led to an error(!=0), or not (=0).
            int returnCode = process.waitFor();
            System.out.println("Return code = " + returnCode);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return capturedCommandOutput;
    }


    /**
     * source: https://stackoverflow.com/questions/7369664/using-export-in-java
     * @param processBuilder
     * @param varName
     * @param varContent
     * @return
     */
    private static ProcessBuilder setEnvironmentVariable(ProcessBuilder processBuilder){
        String varName = "variableName";
        String varContent = "/mnt/c/testfolder a/";

        Map<String, String> env = processBuilder.environment();
         System.out.println("Setting environment variable "+varName+"="+varContent);
         env.put(varName, varContent);

         processBuilder.environment().put(varName, varContent);

         return processBuilder;
    }
}


class SyncPipe implements Runnable
{   
    /**
     * This class pipes the output of your command to any new input you generated
     * with stdin. For example, suppose you run cp /mnt/c/a.txt /mnt/b/
     * but for some reason you are prompted: "do you really want to copy there yes/no?
     * then you can answer yes since your input is piped to the output of your
     * original command. (At least that is my practical interpretation might be wrong.)
     * @param istrm
     * @param ostrm
     */
    public SyncPipe(InputStream istrm, OutputStream ostrm) {
        istrm_ = istrm;
        ostrm_ = ostrm;
    }
    public void run() {

      try
      {
          final byte[] buffer = new byte[1024];
          for (int length = 0; (length = istrm_.read(buffer)) != -1; )
          {
              ostrm_.write(buffer, 0, length);                
              }
          }
          catch (Exception e)
          {
              e.printStackTrace();
          }
      }
      private final OutputStream ostrm_;
      private final InputStream istrm_;
}
于 2019-04-17T17:29:55.510 回答
0

Java 8 的另一个解决方案:

BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
String stdOutStr = stdOut.lines()
                   .collect(Collectors.joining(System.lineSeparator()));
于 2021-11-05T10:32:55.483 回答