Ú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.