Útmutató a java.lang.ProcessBuilder API-hoz

1. Áttekintés

A Process API hatékony módot kínál az operációs rendszer parancsainak Java-futtatására. Számos lehetősége van azonban, amelyek nehézkessé tehetik a munkát.

Ebben az oktatóanyagban megnézzük, hogy a Java hogyan enyhíti ezt a ProcessBuilder API.

2. ProcessBuilder API

A ProcessBuilder osztály módszereket kínál az operációs rendszer folyamatainak létrehozására és konfigurálására. Minden egyes ProcessBuilder példány lehetővé teszi számunkra a folyamatattribútumok gyűjteményének kezelését. Ezután kezdhetünk egy újat Folyamat azokkal a megadott tulajdonságokkal.

Íme néhány gyakori eset, amikor ezt az API-t használhatnánk:

  • Keresse meg az aktuális Java verziót
  • Állítson be egyéni kulcs-érték térképet a környezetünkhöz
  • Módosítsa a shell parancs futási könyvtárát
  • Átirányítja a bemeneti és kimeneti adatfolyamokat egyedi helyettesítésekre
  • Örökli a jelenlegi JVM folyamat mindkét folyamatát
  • Végezzen el egy shell parancsot Java-kódból

A későbbi szakaszokban áttekintjük ezek gyakorlati példáit.

Mielőtt azonban belevetnénk magunkat a működő kódba, nézzük meg, hogy az API milyen funkcionalitást nyújt.

2.1. Módszer összefoglalása

Ebben a részben, visszalépünk és röviden áttekintjük a ProcessBuilder osztály. Ez segíteni fog nekünk, amikor később néhány valódi példába merülünk:

  • ProcessBuilder (karakterlánc ... parancs)

    Egy új folyamatkészítő létrehozásához a megadott operációs rendszer programmal és argumentumokkal ezt a kényelmes konstruktort használhatjuk.

  • könyvtár (fájl könyvtár)

    Az aktuális folyamat alapértelmezett munkakönyvtárát felülírhatjuk a Könyvtár módszer és átadás a File tárgy. Alapértelmezés szerint az aktuális munkakönyvtár értéke a user.dir rendszer tulajdonság.

  • környezet()

    Ha meg akarjuk szerezni az aktuális környezeti változókat, akkor egyszerűen meghívhatjuk a környezet módszer. Visszaadja nekünk az aktuális folyamatkörnyezet másolatát System.getenv () hanem mint a Térkép .

  • örökség ()

    Ha meg akarjuk adni, hogy az alfolyamat szabványos I / O forrásának és céljának meg kell egyeznie a jelenlegi Java folyamatéval, használhatjuk a öröklődik módszer.

  • redirectInput (fájl), redirectOutput (fájl), redirectError (fájl)

    Amikor át akarjuk irányítani a folyamatépítő szabványos bemeneti, kimeneti és hibacélját egy fájlba, ez a három hasonló átirányítási módszer áll rendelkezésünkre.

  • Rajt()

    Végül, de nem utolsósorban, hogy új folyamatot kezdjünk a konfiguráltakkal, egyszerűen felhívjuk Rajt().

Meg kell jegyeznünk, hogy ez az osztály NEM szinkronizált. Például, ha több szálunk van a ProcessBuilder példány egyidejűleg, akkor a szinkronizálást külsőleg kell kezelni.

3. Példák

Most, hogy alapvető ismereteink vannak a ProcessBuilder API, nézzünk át néhány példát.

3.1. Használata ProcessBuilder a Java verziójának kinyomtatásához

Ebben az első példában a Jáva parancsot egy argumentummal a verzió megszerzéséhez.

Process process = new ProcessBuilder ("java", "-verzió"). Start ();

Először létrehozzuk a sajátunkat ProcessBuilder objektum átadja a parancsot és az argumentum értékeket a konstruktornak. Ezután elindítjuk a folyamatot a Rajt() módszer a Folyamat tárgy.

Most nézzük meg, hogyan kell kezelni a kimenetet:

Eredmények felsorolása = readOutput (process.getInputStream ()); assertThat ("Az eredmények nem lehetnek üresek", az eredmények (nem (üres ()))); assertThat ("Az eredményeknek tartalmaznia kell a java verziót:", results, hasItem (tartalmazString ("java verzió"))); int exitCode = process.waitFor (); assertEquals ("Nem szabad hibát észlelni", 0, exitCode);

Itt olvassuk a folyamat kimenetét, és ellenőrizzük a várt tartalmat. Az utolsó lépésben megvárjuk, amíg a folyamat befejeződik process.waitFor ().

Miután a folyamat befejeződött, a visszatérési érték megmondja, hogy a folyamat sikeres volt-e vagy sem.

Néhány fontos szempont, amelyet szem előtt kell tartani:

  • Az érveknek megfelelő sorrendben kell lenniük
  • Sőt, ebben a példában az alapértelmezett munkakönyvtárat és környezetet használjuk
  • Szándékosan nem hívunk process.waitFor () amíg nem olvassuk a kimenetet, mert a kimeneti puffer leállíthatja a folyamatot
  • Feltételeztük, hogy a Jáva parancs a PÁLYA változó

3.2. Folyamat indítása módosított környezettel

Ebben a következő példában megnézzük, hogyan lehet módosítani a munkakörnyezetet.

De mielőtt ezt megtennénk, kezdjük azzal, hogy megvizsgáljuk, milyen információkat találhatunk az alapértelmezett környezetben:

ProcessBuilder processBuilder = new ProcessBuilder (); Térkép környezet = processBuilder.environment (); environment.forEach ((kulcs, érték) -> System.out.println (kulcs + érték));

Ez egyszerűen kinyomtatja az alapértelmezés szerint megadott összes változó bejegyzést:

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

Most hozzá fogunk adni egy új környezeti változót ProcessBuilder objektumot, és futtasson egy parancsot annak értékének kiadásához:

environment.put ("ÜDVÖZLÉS", "Hola Mundo"); processBuilder.command ("/ bin / bash", "-c", "echo $ GREETING"); Process process = processBuilder.start ();

Bontjuk le a lépéseket, hogy megértsük, mit tettünk:

  • Adjon hozzá egy „GREETING” nevű változót egy „Hola Mundo” értékkel a környezetünkhöz, amely szabvány Térkép
  • Ezúttal a konstruktor használata helyett a parancsot és az argumentumokat a parancs (karakterlánc… parancs) módszerrel.
  • Ezután megkezdjük a folyamatot az előző példa szerint.

A példa befejezéséhez ellenőrizzük, hogy a kimenet üdvözletünket tartalmazza:

Eredmények felsorolása = readOutput (process.getInputStream ()); assertThat ("Az eredmények nem lehetnek üresek", az eredmények (nem (üres ()))); assertThat ("Az eredményeknek tartalmaznia kell a java verziót:", results, hasItem (tartalmazString ("Hola Mundo")));

3.3. Folyamat indítása módosított munkakönyvtárral

Néha hasznos lehet megváltoztatni a munkakönyvtárat. A következő példánkban meglátjuk, hogyan kell ezt megtenni:

@Test public void givenProcessBuilder_whenModifyWorkingDir_thenSuccess () dobja az IOException, InterruptedException {ProcessBuilder processBuilder = new ProcessBuilder ("/ bin / sh", "-c", "ls"); processBuilder.directory (új Fájl ("src")); Process process = processBuilder.start (); Eredmények felsorolása = readOutput (process.getInputStream ()); assertThat ("Az eredmények nem lehetnek üresek", az eredmények (nem (üres ()))); assertThat ("Az eredményeknek tartalmazniuk kell könyvtárlistát:", results, tartalmaz ("main", "test")); int exitCode = process.waitFor (); assertEquals ("Nem szabad hibát észlelni", 0, exitCode); }

A fenti példában a munkakönyvtárat a projekthez állítottuk be src dir a kényelmi módszerrel könyvtár (fájl könyvtár). Ezután futtatunk egy egyszerű könyvtárlista parancsot, és ellenőrizzük, hogy a kimenet tartalmazza-e az alkönyvtárakat fő- és teszt.

3.4. A standard bemenet és kimenet átirányítása

A való világban valószínűleg egy naplófájlba szeretnénk rögzíteni futó folyamataink eredményeit további elemzés céljából. Szerencsére a ProcessBuilder Az API pontosan ehhez beépített támogatással rendelkezik, amint ezt a példában láthatjuk.

Alapértelmezés szerint folyamatunk egy cső bemenetét olvassa le. Ehhez a csőhöz a kimeneti áram által juthatunk vissza Process.getOutputStream ().

Azonban, amint hamarosan látni fogjuk, a szabványos kimenetet át lehet irányítani egy másik forrásba, például egy fájlba a módszer segítségével redirectOutput. Ebben az esetben, getOutputStream () visszaadja a ProcessBuilder.NullOutputStream.

Térjünk vissza az eredeti példánkra, hogy kinyomtassuk a Java verzióját. De ezúttal irányítsuk át a kimenetet egy log fájlba a normál kimeneti cső helyett:

ProcessBuilder processBuilder = new ProcessBuilder ("java", "-verzió"); processBuilder.redirectErrorStream (true); Fájlnapló = folder.newFile ("java-version.log"); processBuilder.redirectOutput (napló); Process process = processBuilder.start ();

A fenti példában létrehozunk egy új ideiglenes fájlt log néven, és elmondjuk a sajátunknak ProcessBuilder a kimenet átirányításához erre a fájlcélra.

Ebben az utolsó részletben egyszerűen ellenőrizzük getInputStream () valóban nulla és hogy fájlunk tartalma a vártnak felel meg:

assertEquals ("Átirányításkor -1-nek kell lennie, -1, process.getInputStream (). read ()); Sorok felsorolása = Files.lines (log.toPath ()). Gyűjt (Collectors.toList ()); assertThat ("Az eredményeknek tartalmaznia kell a java verziót:", vonalak, hasItem (tartalmazString ("java verzió")));

Most nézzünk meg egy kis változatot ebben a példában. Például, ha egy naplófájlhoz szeretnénk csatolni, ahelyett, hogy minden alkalommal újat hoznánk létre:

Fájlnapló = tempFolder.newFile ("java-version-append.log"); processBuilder.redirectErrorStream (true); processBuilder.redirectOutput (Redirect.appendTo (log));

Fontos megemlíteni a hívást is redirectErrorStream (true). Bármely hiba esetén a hiba kimenete beolvad a normál folyamat kimeneti fájlba.

Természetesen megadhatunk egyedi fájlokat a standard kimenethez és a standard hibakimenethez:

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

3.5. Az aktuális folyamat I / O öröklése

Ebben az utolsó előtti példában a örökség () módszer működésben. Akkor használhatjuk ezt a módszert, amikor az alfolyamat I / O-t át akarjuk irányítani az aktuális folyamat standard I / O-jára:

@Test public void givenProcessBuilder_whenInheritIO_thenSuccess () dobja az IOException, InterruptedException {ProcessBuilder processBuilder = new ProcessBuilder ("/ bin / sh", "-c", "echo hello"); processBuilder.inheritIO (); Process process = processBuilder.start (); int exitCode = process.waitFor (); assertEquals ("Nem szabad hibát észlelni", 0, exitCode); }

A fenti példában a örökség () metódus egy egyszerű parancs kimenetét látjuk az IDE konzolján.

A következő részben megvizsgáljuk, hogy milyen kiegészítések történtek a ProcessBuilder API a Java 9-ben.

4. Java 9 kiegészítések

A Java 9 bevezette a csővezetékek fogalmát a ProcessBuilder API:

public static List startPipeline (Lista készítők) 

Használni a startPipeline módszerrel átadhatunk egy listát ProcessBuilder tárgyakat. Ez a statikus módszer ekkor elindítja a Folyamat az egyes ProcessBuilder. Így olyan folyamatok létrehozása, amelyek összekapcsolódnak a standard kimenetükkel és a standard bemeneti folyamaikkal.

Például, ha ilyesmit akarunk futtatni:

megtalálja . -név * .java -type f | wc -l

Amit tennénk, létrehoznánk egy folyamatkészítőt az egyes elkülönített parancsokhoz, és összeállítanánk őket egy csővezetékbe:

@Test public void givenProcessBuilder_whenStartingPipeline_thenSuccess () dobja az IOException, InterruptedException {List builders = Arrays.asList (new ProcessBuilder ("find", "src", "-name", "* .java", "-type", "f") , új ProcessBuilder ("wc", "-l")); Folyamatlista = ProcessBuilder.startPipeline (építők); Process last = process.get (process.size () - 1); Lista kimenet = readOutput (last.getInputStream ()); assertThat ("Az eredmények nem lehetnek üresek", a kimenet az (nem (üres ()))); }

Ebben a példában az összes java fájlt megkeressük a src könyvtárba, és az eredményeket egy másik folyamatba továbbítja, hogy megszámolja őket.

A Java 9 Process API egyéb fejlesztéseiről a Java 9 Process API Improvements című nagy cikkünkben olvashat.

5. Következtetés

Összefoglalva, ebben az oktatóanyagban feltártuk a java.lang.ProcessBuilder API részletesen.

Először azzal kezdtük, hogy elmagyaráztuk, mit lehet tenni az API-val, és összefoglaltuk a legfontosabb módszereket.

Ezután számos gyakorlati példát vettünk szemügyre. Végül megvizsgáltuk, milyen új kiegészítéseket vezettek be az API-ban a Java 9-ben.

Mint mindig, a cikk teljes forráskódja elérhető a GitHubon.