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

87

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

我正在使用如下代码启动一个进程构建器,我想知道如何将它的输出重定向到一个String

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

我尝试使用 ByteArrayOutputStream,但好像没有起作用。


你如何使用 ByteArrayOutputStream - fGo
“它似乎没有起作用”不是问题描述。“ProcessBuilder”没有流,但“Process”有。你没有启动“ProcessBuilder”,而是使用它来创建一个“Process”,然后启动。要精确。 - user207421
13个回答

1
这是与其他答案补充的信息,在我的情况下非常重要。
我会随机地得不到输出,因为我的进程需要一些时间才能完成。
使用process.waitFor方法并不能解决问题。
解决我的问题的方法是在读取输入流之前使用:while (process.isAlive());

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://dev59.com/r2Pcs4cB2Jgan1znEbSI
     * @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_;
}

0
这是给那些使用 Kotlin 的用户的提示:
val myCommand = "java -version"
val process = ProcessBuilder()
    .command(myCommand.split(" "))
    // .directory(File("./")) // Working directory
    .redirectOutput(ProcessBuilder.Redirect.INHERIT)
    .redirectError(ProcessBuilder.Redirect.INHERIT)
    .start()
process.waitFor(60, TimeUnit.SECONDS)
val result = process.inputStream.reader().readText()
println(result)

@Johannes_Barop 感谢您的编辑。我尝试运行上述代码,但出现了一些问题。 - Mahozad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接