Hoe kan ik de output van ProcessBuilder omleiden naar een string?

Ik gebruik de volgende code om een ​​procesbouwer te starten. Ik wil weten hoe ik de uitvoer kan omleiden naar een String.

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

Ik heb geprobeerd ByteArrayOutputStreamte gebruiken, maar het lijkt niet te werken.


Antwoord 1, autoriteit 100%

Lees uit de InputStream. U kunt de uitvoer toevoegen aan een 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();

Antwoord 2, autoriteit 32%

Apache Commons gebruiken IOUTilsje kunt het in één regel doen:

ProcessBuilder pb = new ProcessBuilder("pwd");
String output = IOUtils.toString(pb.start().getInputStream(), StandardCharsets.UTF_8);

Antwoord 3, autoriteit 12%

Java 8-voorbeeld:

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;
}

Gebruik:

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

Ik gebruik deze exacte code en het werkt goed voor resultaten met één of meerdere regels. Je zou ook een foutstroom-handler kunnen toevoegen.


Antwoord 4, autoriteit 10%

Je zou zoiets als dit kunnen doen:

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);
}

Antwoord 5, autoriteit 9%

Vanaf Java 9hebben we eindelijk een oneliner:

ProcessBuilder pb = new ProcessBuilder("pwd");
Process process = pb.start();
String result = new String(process.getInputStream().readAllBytes());

Antwoord 6, autoriteit 5%

Voeg gewoon .inheritIO();toe aan de procesbouwer-regel.

IE:

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


Antwoord 7, autoriteit 5%

Voor Java 7 en 8 zou dit moeten werken:

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

Doe dan in uw code:

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

Ik heb dat schaamteloos gestolen van: Hoe kan de procesbouwer worden doorverwezen uitvoer naar een string?


Antwoord 8, Autoriteit 2%

In Java 8 is er een lekkere lijnen () stream die u kunt combineren met string.join en systeem.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())
    }

Antwoord 9, Autoriteit 2%

Na het proberen om verschillende gevallen aan te pakken (zowel Stderr en Stdout afhandelen en het blokkeren van een van deze, het beëindigen van het proces na time-out, ontsnapt het goed ontsnappen aan schuine strepen, offertekens, speciale tekens, spaties, ….) Ik gaf het op en vond ik Apache Commons Exec https://commons.apache.org/ Juiste / Commons-Exec / Tutorial.html die al deze dingen redelijk goed doen.

Ik raad iedereen aan die nodig is om het externe proces in Java aan te roepen om Apache Commons Exec-bibliotheek te gebruiken in plaats van het opnieuw opnieuw uit te vinden.


Antwoord 10

oplossingen

  • Deze code is een actief voorbeeld voor de algemene oplossing voor uw vraag:

Hoe de uitvoer van Process Builder naar een string omleiden?

  • Credits Ga naar Greg T, nadat u meerdere oplossingen hebt geprobeerd om verschillende opdrachten uit te voeren en hun outputs van GReg T’s antwoord vast te leggen, bevatte de essentie van de specifieke oplossing. Ik hoop dat het algemene voorbeeld van nut is voor iemand die meerdere vereisten combineert tijdens het vastleggen van de uitvoer.
  • Om uw specifieke oplossing te verkrijgen, kunt u eenheid ProcessBuilder pb = new ProcessBuilder(System.getProperty("user.dir")+"/src/generate_list.sh", filename);, UncoMent regel en reageer op: ProcessBuilder processBuilder = new ProcessBuilder(commands);.

functionaliteit

  • Het is een werkvoorbeeld dat commando wordt uitgevoerd echo 1en retourneert de uitvoer als een tekenreeks.
  • Ik heb ook een werkpad en een omgevingsvariabele toegevoegd, die niet vereist is voor uw specifieke voorbeeld, zodat u deze kunt verwijderen.

gebruik & amp; verificatie

  • U kunt deze code als een klasse kopiëren, compileren op de pot en het uitvoeren.
  • Het is geverifieerd in WSL Ubuntu 16.04.
  • Instellen van de werkdirectory wordt geverifieerd door binaryCommand[0]="touch";en binaryCommand[1]="1";, opnieuw samenstellen en rennen .jarbestand.

beperkingen

  • Als de pijp vol is (vanwege een “te grote” uitvoer), hangt de code.

code

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_;
}

Antwoord 11

Een andere oplossing voor Java 8:

BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
String stdOutStr = stdOut.lines()
                   .collect(Collectors.joining(System.lineSeparator()));

Other episodes