Vodič za java.lang.ProcessBuilder API

1. Pregled

Procesni API pruža moćan način za izvršavanje naredbi operativnog sustava u Javi. Međutim, ima nekoliko mogućnosti zbog kojih može biti glomazan rad.

U ovom vodiču, mi ćemo pogledati kako Java to ublažava s ProcessBuilder API.

2. ProcessBuilder API

The ProcessBuilder klasa pruža metode za stvaranje i konfiguriranje procesa operativnog sustava. Svaki ProcessBuilder instanca nam omogućuje upravljanje zbirkom procesnih atributa. Tada možemo započeti novi Postupak s tim zadanim atributima.

Evo nekoliko uobičajenih scenarija u kojima bismo mogli koristiti ovaj API:

  • Pronađite trenutnu Javinu verziju
  • Postavite prilagođenu mapu ključ / vrijednost za naše okruženje
  • Promijenite radni direktorij gdje se izvodi naša naredba ljuske
  • Preusmjerite ulazne i izlazne tokove na prilagođene zamjene
  • Naslijedite oba toka trenutnog JVM procesa
  • Izvršite naredbu ljuske iz Java koda

U sljedećim ćemo odjeljcima pogledati praktične primjere za svaki od njih.

No, prije nego što uđemo u radni kod, pogledajmo kakvu funkcionalnost nudi ovaj API.

2.1. Sažetak metode

U ovom odjeljku, vratit ćemo se korak unatrag i ukratko pogledati najvažnije metode u ProcessBuilder razred. To će nam pomoći kad kasnije zaronimo u neke stvarne primjere:

  • ProcessBuilder (naredba String ...)

    Da bismo stvorili novi graditelj procesa s navedenim programom operativnog sustava i argumentima, možemo koristiti ovaj prikladni konstruktor.

  • direktorij (direktorij datoteka)

    Možemo nadjačati zadani radni direktorij trenutnog postupka pozivom imenik metoda i polaganje a Datoteka objekt. Prema zadanim postavkama trenutni radni direktorij postavljen je na vrijednost koju vraća korisnik.dir svojstvo sustava.

  • okoliš()

    Ako želimo dobiti trenutne varijable okruženja, možemo jednostavno nazvati okoliš metoda. Vraća nam kopiju trenutnog procesnog okruženja koje koristi System.getenv () ali kao a Karta .

  • nasleditiIO ()

    Ako želimo navesti da bi izvor i odredište za naš standardni proces I / O podprocesa trebali biti isti kao i trenutni Java proces, možemo koristiti naslijeditiIO metoda.

  • redirectInput (datoteka datoteke), redirectOutput (datoteka datoteke), redirectError (datoteka datoteke)

    Kada želimo preusmjeriti standardni ulaz, izlaz i odredište pogreške graditelja procesa u datoteku, na raspolaganju su nam ove tri slične metode preusmjeravanja.

  • početak()

    I na kraju, ali ne najmanje važno, da bismo započeli novi postupak s onim što smo konfigurirali, jednostavno zovemo početak().

Treba imati na umu da ova klasa NIJE sinkronizirana. Na primjer, ako imamo više niti koje pristupaju a ProcessBuilder instance istodobno, tada se sinkronizacijom mora upravljati izvana.

3. Primjeri

Sad kad imamo osnovno razumijevanje ProcessBuilder API, prođimo kroz neke primjere.

3.1. Koristeći ProcessBuilder za ispis verzije Jave

U ovom prvom primjeru pokrenut ćemo Java naredba s jednim argumentom kako bi se dobila verzija.

Procesni postupak = novi ProcessBuilder ("java", "-verzija"). Start ();

Prvo, mi stvaramo svoje ProcessBuilder objekt koji predaje vrijednosti naredbe i argumenta konstruktoru. Dalje, započinjemo postupak pomoću početak() metoda za dobivanje a Postupak objekt.

Sada da vidimo kako se rukuje izlazom:

Navesti rezultate = readOutput (process.getInputStream ()); assertThat ("Rezultati ne smiju biti prazni", rezultati jesu (nije (prazno ()))); assertThat ("Rezultati trebaju sadržavati java verziju:", results, hasItem (containsString ("java verzija"))); int exitCode = process.waitFor (); assertEquals ("Ne smiju se otkriti pogreške", 0, izlazCode);

Ovdje čitamo izlazne podatke postupka i provjeru sadržaja ono što i očekujemo. U posljednjem koraku čekamo da postupak završi s upotrebom process.waitFor ().

Nakon završetka postupka, povratna vrijednost govori nam je li postupak bio uspješan ili ne.

Nekoliko važnih točaka koje morate imati na umu:

  • Argumenti moraju biti u pravom redoslijedu
  • Štoviše, u ovom se primjeru koriste zadani radni direktorij i okruženje
  • Namjerno ne zovemo process.waitFor () sve dok ne pročitamo izlaz, jer bi izlazni međuspremnik mogao zaustaviti proces
  • Pretpostavili smo da Java naredba je dostupna putem STAZA varijabilna

3.2. Pokretanje procesa s modificiranim okruženjem

U sljedećem ćemo primjeru vidjeti kako modificirati radno okruženje.

No prije nego što to učinimo, krenimo s pogledom na vrstu informacija koje možemo pronaći u zadanom okruženju:

ProcessBuilder processBuilder = novi ProcessBuilder (); Kartirajte okruženje = processBuilder.environment (); environment.forEach ((ključ, vrijednost) -> System.out.println (ključ + vrijednost));

Ovo jednostavno ispisuje svaki od unosa varijabli koji su zadani prema zadanim postavkama:

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

Sada ćemo dodati novu varijablu okruženja u našu ProcessBuilder objekt i pokrenite naredbu za izlaz njegove vrijednosti:

environment.put ("POZDRAV", "Hola Mundo"); processBuilder.command ("/ bin / bash", "-c", "echo $ GREETING"); Procesni postupak = processBuilder.start ();

Razložimo korake da bismo razumjeli što smo učinili:

  • U naše okruženje dodajte varijablu pod nazivom "POZDRAV" s vrijednošću "Hola Mundo", što je standard Karta
  • Ovaj put, umjesto da koristimo konstruktor, naredbu i argumente postavljamo putem naredba (niz ... naredba) metoda izravno.
  • Zatim započinjemo naš postupak prema prethodnom primjeru.

Da bismo upotpunili primjer, provjeravamo da li izlaz sadrži naš pozdrav:

Navesti rezultate = readOutput (process.getInputStream ()); assertThat ("Rezultati ne smiju biti prazni", rezultati jesu (nije (prazno ()))); assertThat ("Rezultati trebaju sadržavati java verziju:", results, hasItem (containsString ("Hola Mundo")));

3.3. Pokretanje procesa s modificiranim radnim imenikom

Ponekad može biti korisno promijeniti radni direktorij. U sljedećem ćemo primjeru vidjeti kako to učiniti:

@Test javna praznina givenProcessBuilder_whenModifyWorkingDir_thenSuccess () baca IOException, InterruptedException {ProcessBuilder processBuilder = novi ProcessBuilder ("/ bin / sh", "-c", "ls"); processBuilder.directory (nova datoteka ("src")); Procesni postupak = processBuilder.start (); Navesti rezultate = readOutput (process.getInputStream ()); assertThat ("Rezultati ne smiju biti prazni", rezultati jesu (nije (prazno ()))); assertThat ("Rezultati trebaju sadržavati popis direktorija:", rezultati, sadrži ("glavni", "test")); int exitCode = process.waitFor (); assertEquals ("Ne smiju se otkriti pogreške", 0, izlazni kod); }

U gornjem primjeru postavili smo radni direktorij na projektni src dir uporabom praktične metode direktorij (direktorij datoteka). Zatim izvodimo jednostavnu naredbu za popis direktorija i provjeravamo sadrži li izlaz poddirektorijume glavni i test.

3.4. Preusmjeravanje standardnih ulaza i izlaza

U stvarnom ćemo svijetu vjerojatno željeti zabilježiti rezultate naših pokrenutih procesa unutar datoteke dnevnika za daljnju analizu. Srećom ProcessBuilder API ima ugrađenu podršku upravo za to, kao što ćemo vidjeti u ovom primjeru.

Prema zadanim postavkama naš postupak čita ulaz iz cijevi. Ovoj cijevi možemo pristupiti putem izlaznog toka koji je vratio Process.getOutputStream ().

Međutim, kao što ćemo vidjeti uskoro, standardni izlaz pomoću metode može se preusmjeriti na drugi izvor, poput datoteke redirectOutput. U ovom slučaju, getOutputStream () vratit će a ProcessBuilder.NullOutputStream.

Vratimo se izvornom primjeru za ispis verzije Jave. Ali ovaj put preusmjerimo izlaz u datoteku dnevnika umjesto u standardnu ​​izlaznu cijev:

ProcessBuilder processBuilder = novi ProcessBuilder ("java", "-verzija"); processBuilder.redirectErrorStream (true); Dnevnik datoteke = folder.newFile ("java-version.log"); processBuilder.redirectOutput (zapisnik); Procesni postupak = processBuilder.start ();

U gornjem primjeru, kreiramo novu privremenu datoteku koja se zove log i kažemo našoj ProcessBuilder za preusmjeravanje izlaza na ovo odredište datoteke.

U ovom posljednjem isječku to jednostavno provjeravamo getInputStream () je doista null te da je sadržaj naše datoteke očekivan:

assertEquals ("Ako se preusmjeri, treba biti -1", -1, process.getInputStream (). read ()); Redovi popisa = Files.lines (log.toPath ()). Collect (Collectors.toList ()); assertThat ("Rezultati trebaju sadržavati java verziju:", linije, hasItem (containsString ("java verzija")));

Pogledajmo sada male varijacije na ovom primjeru. Na primjer, kada se želimo dodati datoteci dnevnika, a ne svaki put stvoriti novu:

Dnevnik datoteke = tempFolder.newFile ("java-version-append.log"); processBuilder.redirectErrorStream (true); processBuilder.redirectOutput (Redirect.appendTo (log));

Također je važno spomenuti poziv na redirectErrorStream (true). U slučaju bilo kakvih pogrešaka, izlaz pogreške spojit će se u normalnu izlaznu datoteku procesa.

Možemo, naravno, odrediti pojedinačne datoteke za standardni izlaz i standardni izlaz pogreške:

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

3.5. Nasljeđivanje I / O trenutnog procesa

U ovom pretposljednjem primjeru vidjet ćemo nasleditiIO () metoda u akciji. Ovu metodu možemo koristiti kada želimo preusmjeriti I / O potprocesa na standardni I / O trenutnog procesa:

@Test javna praznina givenProcessBuilder_whenInheritIO_thenSuccess () baca IOException, InterruptedException {ProcessBuilder processBuilder = new ProcessBuilder ("/ bin / sh", "-c", "echo hello"); processBuilder.inheritIO (); Procesni postupak = processBuilder.start (); int exitCode = process.waitFor (); assertEquals ("Ne smiju se otkriti pogreške", 0, izlazni kod); }

U gornjem primjeru, pomoću nasleditiIO () metodu vidimo izlaz jednostavne naredbe u konzoli u našem IDE-u.

U sljedećem ćemo odjeljku pogledati koji su dodaci dodani ProcessBuilder API u Javi 9.

4. Dodaci Java 9

Java 9 je koncept cjevovoda uveo u ProcessBuilder API:

javni statički popis startPipeline (graditelji popisa) 

Koristiti startPipeline metodu možemo proslijediti popis ProcessBuilder predmeta. Zatim će ova statička metoda pokrenuti a Postupak za svakoga ProcessBuilder. Dakle, stvaranje cjevovoda procesa koji su povezani svojim standardnim izlaznim i standardnim ulaznim tokovima.

Na primjer, ako želimo pokrenuti nešto poput ovoga:

pronaći . -ime * .java -tip f | wc -l

Ono što bismo učinili je stvoriti graditelj procesa za svaku izoliranu naredbu i sastaviti ih u cjevovod:

@Test public void givenProcessBuilder_whenStartingPipeline_thenSuccess () baca IOException, InterruptedException {Builders = Arrays.asList (new ProcessBuilder ("find", "src", "-name", "* .java", "-type", "f") , novi ProcessBuilder ("wc", "-l")); Popis procesa = ProcessBuilder.startPipeline (graditelji); Proces zadnji = procesi.get (procesi.size () - 1); Popis izlaz = readOutput (last.getInputStream ()); assertThat ("Rezultati ne smiju biti prazni", izlaz je (nije (prazan ()))); }

U ovom primjeru tražimo sve java datoteke unutar src direktorija i slanjem rezultata u drugi postupak za njihovo brojanje.

Da biste saznali više o ostalim poboljšanjima u API-ju procesa u Javi 9, pogledajte naš sjajni članak o poboljšanjima API-ja Java 9 procesa.

5. Zaključak

Da rezimiramo, u ovom uputstvu istražili smo java.lang.ProcessBuilder API detaljno.

Prvo smo započeli objašnjavajući što se može učiniti s API-jem i saželi najvažnije metode.

Zatim smo pogledali brojne praktične primjere. Na kraju smo pogledali koji su novi dodaci uvedeni u API u Javi 9.

Kao i uvijek, puni izvorni kôd članka dostupan je na GitHub-u.