Vejledning til java.lang.ProcessBuilder API

1. Oversigt

Process API giver en effektiv måde at udføre operativsystemkommandoer på Java. Det har dog flere muligheder, der kan gøre det besværligt at arbejde med.

I denne vejledning vi vil se på, hvordan Java afhjælper det med ProcessBuilder API.

2. ProcessBuilder API

Det ProcessBuilder klasse giver metoder til oprettelse og konfiguration af operativsystemprocesser. Hver ProcessBuilder eksempel giver os mulighed for at administrere en samling af procesattributter. Vi kan så starte et nyt Behandle med de givne attributter.

Her er et par almindelige scenarier, hvor vi kunne bruge denne API:

  • Find den aktuelle Java-version
  • Opsæt et brugerdefineret nøgleværdikort til vores miljø
  • Skift arbejdskataloget, hvor vores shell-kommando kører
  • Omdiriger input- og outputstrømme til tilpassede udskiftninger
  • Arv begge strømme af den aktuelle JVM-proces
  • Udfør en shell-kommando fra Java-kode

Vi kigger på praktiske eksempler på hver af disse i senere afsnit.

Men inden vi dykker ned i arbejdskoden, lad os se på, hvilken slags funktionalitet denne API giver.

2.1. Metodeoversigt

I dette afsnit, Vi tager et skridt tilbage og kort ser på de vigtigste metoder i ProcessBuilder klasse. Dette hjælper os, når vi dykker ned i nogle reelle eksempler senere:

  • ProcessBuilder (streng ... kommando)

    For at oprette en ny procesbygger med det specificerede operativsystemprogram og argumenter kan vi bruge denne praktiske konstruktør.

  • bibliotek (Filmappe)

    Vi kan tilsidesætte standardarbejdskataloget for den aktuelle proces ved at ringe til vejviser metode og videregivelse af en Fil objekt. Som standard er den aktuelle arbejdsmappe indstillet til den værdi, der returneres af bruger.dir systemegenskab.

  • miljø()

    Hvis vi ønsker at få de aktuelle miljøvariabler, kan vi bare kalde miljø metode. Det returnerer os en kopi af det aktuelle procesmiljø ved hjælp af System.getenv () men som en Kort .

  • inheritIO ()

    Hvis vi vil specificere, at kilden og destinationen til vores underprocesstandard I / O skal være den samme som den aktuelle Java-proces, kan vi bruge arv metode.

  • redirectInput (filfil), redirectOutput (filfil), redirectError (filfil)

    Når vi vil omdirigere procesbyggerens standardinput, output og fejldestination til en fil, har vi disse tre lignende omdirigeringsmetoder til vores rådighed.

  • Start()

    Sidst men ikke mindst, for at starte en ny proces med det, vi har konfigureret, kalder vi simpelthen Start().

Vi skal bemærke, at denne klasse IKKE er synkroniseret. For eksempel, hvis vi har flere tråde, der får adgang til a ProcessBuilder forekomst samtidigt, så skal synkroniseringen styres eksternt.

3. Eksempler

Nu hvor vi har en grundlæggende forståelse af ProcessBuilder API, lad os gå igennem nogle eksempler.

3.1. Ved brug af ProcessBuilder for at udskrive Java-versionen

I dette første eksempel kører vi java kommando med et argument for at få versionen.

Process proces = ny ProcessBuilder ("java", "-version"). Start ();

Først opretter vi vores ProcessBuilder objekt, der sender kommando- og argumentværdierne til konstruktøren. Derefter starter vi processen ved hjælp af Start() metode til at få en Behandle objekt.

Lad os nu se, hvordan man håndterer output:

Listeresultater = readOutput (process.getInputStream ()); assertThat ("Resultaterne skal ikke være tomme", resultater, er (ikke (tomme ()))); assertThat ("Resultaterne skal indeholde java-version:", resultater, hasItem (indeholderString ("java-version"))); int exitCode = process.waitFor (); assertEquals ("Ingen fejl skal detekteres", 0, exitCode);

Her læser vi procesoutputtet og verificerer indholdet, som vi forventer. I det sidste trin venter vi på, at processen er færdig med at blive brugt process.waitFor ().

Når processen er afsluttet, fortæller returværdien os, om processen var vellykket eller ej.

Et par vigtige punkter at huske på:

  • Argumenterne skal være i den rigtige rækkefølge
  • Desuden bruges standardarbejdskataloget og miljøet i dette eksempel
  • Vi ringer bevidst ikke process.waitFor () indtil efter at vi har læst output, fordi outputbufferen muligvis stopper processen
  • Vi har antaget, at java kommandoen er tilgængelig via STI variabel

3.2. Start af en proces med et ændret miljø

I dette næste eksempel vil vi se, hvordan du ændrer arbejdsmiljøet.

Men inden vi gør det, lad os begynde med at se på den slags information, vi kan finde i standardmiljøet:

ProcessBuilder processBuilder = ny ProcessBuilder (); Kortmiljø = processBuilder.environment (); miljø.forEach ((nøgle, værdi) -> System.out.println (nøgle + værdi));

Dette udskriver simpelthen hver af de variable poster, der leveres som standard:

PATH / usr / bin: / bin: / usr / sbin: / sbin SHELL / bin / bash ...

Nu skal vi tilføje en ny miljøvariabel til vores ProcessBuilder objekt og kør en kommando for at output sin værdi:

environment.put ("Hilsen", "Hola Mundo"); processBuilder.command ("/ bin / bash", "-c", "echo $ GREETING"); Process proces = processBuilder.start ();

Lad os nedbryde trinene for at forstå, hvad vi har gjort:

  • Tilføj en variabel kaldet 'Hilsen' med værdien 'Hola Mundo' til vores miljø, som er en standard Kort
  • Denne gang, i stedet for at bruge konstruktøren, indstiller vi kommandoen og argumenterne via kommando (streng ... kommando) metode direkte.
  • Vi starter derefter vores proces som i det foregående eksempel.

For at fuldføre eksemplet kontrollerer vi, at outputen indeholder vores hilsen:

Listeresultater = readOutput (process.getInputStream ()); assertThat ("Resultaterne skal ikke være tomme", resultater, er (ikke (tomme ()))); assertThat ("Resultaterne skal indeholde java-version:", resultater, hasItem (indeholderString ("Hola Mundo")));

3.3. Start af en proces med en ændret arbejdsmappe

Nogle gange kan det være nyttigt at ændre arbejdsmappen. I vores næste eksempel skal vi se, hvordan man gør netop det:

@Test offentlig ugyldighed givenProcessBuilder_whenModifyWorkingDir_thenSuccess () kaster IOException, InterruptedException {ProcessBuilder processBuilder = ny ProcessBuilder ("/ bin / sh", "-c", "ls"); processBuilder.directory (ny fil ("src")); Process proces = processBuilder.start (); Listeresultater = readOutput (process.getInputStream ()); assertThat ("Resultaterne skal ikke være tomme", resultater, er (ikke (tomme ()))); assertThat ("Resultaterne skal indeholde katalogoversigt:", resultater, indeholder ("hoved", "test")); int exitCode = process.waitFor (); assertEquals ("Ingen fejl skal detekteres", 0, exitCode); }

I ovenstående eksempel indstiller vi arbejdsmappen til projektets src dir ved hjælp af bekvemmelighed metoden bibliotek (Filmappe). Vi kører derefter en simpel kataloglistekommando og kontrollerer, at output indeholder underkataloger vigtigste og prøve.

3.4. Omdirigering af standard input og output

I den virkelige verden vil vi sandsynligvis indfange resultaterne af vores kørende processer i en logfil til yderligere analyse. Heldigvis ProcessBuilder API har indbygget support til netop dette, som vi vil se i dette eksempel.

Som standard læser vores proces input fra et rør. Vi kan få adgang til dette rør via den outputstrøm, der returneres af Process.getOutputStream ().

Som vi snart vil se, kan standardoutputtet imidlertid omdirigeres til en anden kilde, f.eks. En fil ved hjælp af metoden redirectOutput. I dette tilfælde, getOutputStream () vil returnere en ProcessBuilder.NullOutputStream.

Lad os vende tilbage til vores oprindelige eksempel for at udskrive versionen af ​​Java. Men lad os denne gang omdirigere output til en logfil i stedet for standardoutputrøret:

ProcessBuilder processBuilder = ny ProcessBuilder ("java", "-version"); processBuilder.redirectErrorStream (sand); File log = folder.newFile ("java-version.log"); processBuilder.redirectOutput (log); Process proces = processBuilder.start ();

I ovenstående eksempel vi opretter en ny midlertidig fil kaldet log og fortæller vores ProcessBuilder for at omdirigere output til denne fildestination.

I dette sidste uddrag kontrollerer vi det bare getInputStream () er faktisk nul og at indholdet af vores fil er som forventet:

assertEquals ("Hvis omdirigeret, skal være -1", -1, process.getInputStream (). læs ()); Liste linjer = Files.lines (log.toPath ()). Indsamle (Collectors.toList ()); assertThat ("Resultaterne skal indeholde java version:", linjer, hasItem (indeholderString ("java version")));

Lad os nu se på en lille variation af dette eksempel. For eksempel når vi ønsker at føje til en logfil i stedet for at oprette en ny hver gang:

Fillog = tempFolder.newFile ("java-version-append.log"); processBuilder.redirectErrorStream (sand); processBuilder.redirectOutput (Redirect.appendTo (log));

Det er også vigtigt at nævne opkaldet til redirectErrorStream (sand). I tilfælde af fejl flettes fejloutputtet i den normale procesoutputfil.

Vi kan selvfølgelig specificere individuelle filer til standardoutput og standardfejloutput:

File outputLog = tempFolder.newFile ("standard-output.log"); Fil errorLog = tempFolder.newFile ("error.log"); processBuilder.redirectOutput (Redirect.appendTo (outputLog)); processBuilder.redirectError (Redirect.appendTo (errorLog));

3.5. Arve I / O i den nuværende proces

I dette næstsidste eksempel ser vi inheritIO () metode i aktion. Vi kan bruge denne metode, når vi vil omdirigere delproces-I / O til standard-I / O for den aktuelle proces:

@Test offentlig ugyldighed givenProcessBuilder_whenInheritIO_thenSuccess () kaster IOException, InterruptedException {ProcessBuilder processBuilder = ny ProcessBuilder ("/ bin / sh", "-c", "ekko hej"); processBuilder.inheritIO (); Process proces = processBuilder.start (); int exitCode = process.waitFor (); assertEquals ("Ingen fejl skal detekteres", 0, exitCode); }

I ovenstående eksempel ved hjælp af inheritIO () metode ser vi output fra en simpel kommando i konsollen i vores IDE.

I det næste afsnit skal vi se på, hvilke tilføjelser der blev foretaget til ProcessBuilder API i Java 9.

4. Java 9-tilføjelser

Java 9 introducerede konceptet med rørledninger til ProcessBuilder API:

offentlig statisk liste startPipeline (listebyggere) 

Bruger startPipeline metode, vi kan sende en liste over ProcessBuilder genstande. Denne statiske metode starter derefter a Behandle for hver ProcessBuilder. Således oprettes en pipeline af processer, der er forbundet med deres standardoutput og standardinputstrømme.

For eksempel, hvis vi vil køre noget som dette:

finde . -navn * .java -type f | wc -l

Hvad vi ville gøre er at oprette en procesbygger for hver isolerede kommando og komponere dem i en pipeline:

@Test offentlig ugyldighed givenProcessBuilder_whenStartingPipeline_thenSuccess () kaster IOException, InterruptedException {List builders = Arrays.asList (new ProcessBuilder ("find", "src", "-name", "* .java", "-type", "f") , ny ProcessBuilder ("wc", "-l")); Listeprocesser = ProcessBuilder.startPipeline (builders); Process last = processer.get (processer.størrelse () - 1); Listeoutput = readOutput (last.getInputStream ()); assertThat ("Resultaterne skal ikke være tomme", output, er (ikke (tom ()))); }

I dette eksempel søger vi efter alle java-filer inde i src katalog og rør resultaterne i en anden proces for at tælle dem.

For at lære om andre forbedringer, der er foretaget i Process API i Java 9, skal du tjekke vores fantastiske artikel om forbedringer af Java 9 Process API.

5. Konklusion

For at opsummere har vi i denne vejledning undersøgt java.lang.ProcessBuilder API i detaljer.

Først startede vi med at forklare, hvad der kan gøres med API'et og opsummerede de vigtigste metoder.

Dernæst kiggede vi på en række praktiske eksempler. Endelig så vi på, hvilke nye tilføjelser der blev introduceret til API'en i Java 9.

Som altid er artiklens fulde kildekode tilgængelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found