Sprievodca rozhraním java.lang.ProcessBuilder API
1. Prehľad
Procesové rozhranie API poskytuje výkonný spôsob vykonávania príkazov operačného systému v prostredí Java. Má však niekoľko možností, vďaka ktorým môže byť ťažkopádne pracovať.
V tomto návode sa pozrieme na to, ako to Java zmierňuje pomocou ProcessBuilder API.
2. ProcessBuilder API
The ProcessBuilder trieda poskytuje metódy na vytváranie a konfiguráciu procesov operačného systému. Každý ProcessBuilder inštancia nám umožňuje spravovať kolekciu atribútov procesu. Potom môžeme začať nový Proces s týmito danými atribútmi.
Tu uvádzame niekoľko bežných scenárov, kde by sme mohli použiť toto API:
- Nájdite aktuálnu verziu Java
- Pripravte si vlastnú mapu kľúč - hodnota pre naše prostredie
- Zmeňte pracovný adresár, kde je spustený náš príkaz shellu
- Presmerujte vstupné a výstupné toky na vlastné náhrady
- Zdediť oba prúdy aktuálneho procesu JVM
- Vykonajte príkaz shellu z kódu Java
V nasledujúcich častiach sa pozrieme na praktické príklady každého z nich.
Ale predtým, ako sa ponoríme do pracovného kódu, poďme sa pozrieť na to, akú funkcionalitu toto API poskytuje.
2.1. Zhrnutie metódy
V tejto časti, urobíme krok späť a v krátkosti sa pozrieme na najdôležitejšie metódy v ProcessBuilder trieda. To nám pomôže, keď sa neskôr ponoríme do niekoľkých skutočných príkladov:
ProcessBuilder (príkaz String ...)
Na vytvorenie nového nástroja na tvorbu procesov so zadaným programom a argumentmi operačného systému môžeme použiť tento pohodlný konštruktor.
adresár (adresár súborov)
Môžeme prepísať predvolený pracovný adresár aktuálneho procesu volaním adresár metóda a absolvovanie a Súbor objekt. Predvolene je aktuálny pracovný adresár nastavený na hodnotu vrátenú parametrom pouzivatel.dir systémový majetok.
prostredie ()
Ak chceme získať súčasné premenné prostredia, môžeme jednoducho zavolať prostredie metóda. Vráti nám kópiu súčasného procesného prostredia, ktoré používa System.getenv () ale ako Mapa .
inheritIO ()
Ak chceme určiť, že zdroj a cieľ nášho štandardného I / O štandardu podprocesu by mali byť rovnaké ako v prípade súčasného procesu Java, môžeme použiť dedičstvo metóda.
redirectInput (súbor súboru), redirectOutput (súbor súborov), redirectError (súbor súborov)
Ak chceme presmerovať štandardný vstup, výstup a cieľ chyby nástroja na tvorbu procesu na súbor, máme k dispozícii tieto tri podobné metódy presmerovania.
štart ()
V neposlednom rade na spustenie nového procesu s tým, čo sme nakonfigurovali, jednoducho zavoláme štart ().
Mali by sme poznamenať, že táto trieda NIE JE synchronizovaná. Napríklad, ak máme viac vlákien prístupových k a ProcessBuilder inštancia súčasne, potom musí byť synchronizácia riadená externe.
3. Príklady
Teraz, keď máme základné znalosti o ProcessBuilder API, prejdime si niekoľko príkladov.
3.1. Použitím ProcessBuilder na vytlačenie verzie Java
V tomto prvom príklade spustíme program java príkaz s jedným argumentom za účelom získania verzie.
Proces process = new ProcessBuilder ("java", "-version"). Start ();
Najskôr vytvoríme náš ProcessBuilder objekt odovzdávajúci hodnoty príkazu a argumentu konštruktoru. Ďalej začneme proces pomocou štart () metóda na získanie a Proces objekt.
Teraz sa pozrime, ako zvládnuť výstup:
Zoznam výsledkov = readOutput (process.getInputStream ()); assertThat ("Výsledky by nemali byť prázdne", výsledky, sú (nie (prázdne ()))); assertThat ("Výsledky by mali obsahovať java verziu:", results, hasItem (containsString ("java verzia"))); int exitCode = process.waitFor (); assertEquals ("Nemali by sa zistiť žiadne chyby", 0, exitCode);
Tu čítame výstup procesu a overujeme, či je obsah taký, aký očakávame. V poslednom kroku čakáme na dokončenie procesu process.waitFor ().
Po dokončení procesu nám návratová hodnota hovorí, či bol proces úspešný alebo nie.
Je potrebné pamätať na niekoľko dôležitých bodov:
- Argumenty musia byť v správnom poradí
- V tomto príklade sa navyše použije predvolený pracovný adresár a prostredie
- Schválne sa neozývame process.waitFor () až po prečítaní výstupu, pretože výstupná vyrovnávacia pamäť môže zastaviť proces
- Urobili sme predpoklad, že java príkaz je k dispozícii prostredníctvom CESTA premenná
3.2. Spustenie procesu s upraveným prostredím
V tomto ďalšom príklade sa pozrieme na to, ako upraviť pracovné prostredie.
Ale predtým, ako to urobíme, začnime pohľadom na druh informácií, ktoré môžeme nájsť v predvolenom prostredí:
ProcessBuilder processBuilder = nový ProcessBuilder (); Mapové prostredie = processBuilder.environment (); environment.forEach ((kľúč, hodnota) -> System.out.println (kľúč + hodnota));
Toto jednoducho vytlačí každú z premenných položiek, ktoré sú poskytované v predvolenom nastavení:
PATH / usr / bin: / bin: / usr / sbin: / sbin SHELL / bin / bash ...
Teraz do našej pridáme novú premennú prostredia ProcessBuilder objekt a spustením príkazu na výstup jeho hodnoty:
environment.put ("GREETING", "Hola Mundo"); processBuilder.command ("/ bin / bash", "-c", "echo $ GREETING"); Proces proces = processBuilder.start ();
Poďme si rozložiť kroky, aby sme pochopili, čo sme urobili:
- Pridajte do nášho prostredia premennú nazvanú „GREETING“ s hodnotou „Hola Mundo“, ktorá je štandardom Mapa
- Tentokrát namiesto použitia konštruktora sme nastavili príkaz a argumenty pomocou príkaz (reťazec ... príkaz) metóda priamo.
- Potom začneme náš proces podľa predchádzajúceho príkladu.
Na dokončenie príkladu overíme, že výstup obsahuje náš pozdrav:
Zoznam výsledkov = readOutput (process.getInputStream ()); assertThat ("Výsledky by nemali byť prázdne", výsledky, sú (nie (prázdne ()))); assertThat ("Výsledky by mali obsahovať java verziu:", results, hasItem (containsString ("Hola Mundo")));
3.3. Spustenie procesu so zmeneným pracovným adresárom
Niekedy môže byť užitočné zmeniť pracovný adresár. V našom ďalšom príklade uvidíme, ako to urobiť:
@ Test public void givenProcessBuilder_whenModifyWorkingDir_thenSuccess () vyvolá IOException, InterruptedException {ProcessBuilder processBuilder = nový ProcessBuilder ("/ bin / sh", "-c", "ls"); processBuilder.directory (nový súbor ("src")); Proces proces = processBuilder.start (); Zoznam výsledkov = readOutput (process.getInputStream ()); assertThat ("Výsledky by nemali byť prázdne", výsledky, sú (nie (prázdne ()))); assertThat ("Výsledky by mali obsahovať zoznam adresárov:", results, contains ("main", "test")); int exitCode = process.waitFor (); assertEquals ("Nemali by sa zistiť žiadne chyby", 0, exitCode); }
Vo vyššie uvedenom príklade sme nastavili pracovný adresár na projekt src d pomocou pohodlnej metódy adresár (adresár súborov). Potom spustíme jednoduchý príkaz na výpis adresárov a skontrolujeme, či výstup obsahuje podadresáre hlavný a test.
3.4. Presmerovanie štandardného vstupu a výstupu
V reálnom svete pravdepodobne budeme chcieť zachytiť výsledky našich bežiacich procesov v protokolovom súbore na ďalšiu analýzu. Našťastie ProcessBuilder API má zabudovanú podporu presne pre toto, ako uvidíme v tomto príklade.
V predvolenom nastavení náš proces číta vstup z potrubia. K tomuto kanálu môžeme pristupovať cez výstupný prúd vrátený serverom Process.getOutputStream ().
Ako však čoskoro uvidíme, štandardný výstup môže byť pomocou metódy presmerovaný na iný zdroj, napríklad do súboru redirectOutput. V tomto prípade, getOutputStream () vráti a ProcessBuilder.NullOutputStream.
Vráťme sa k nášmu pôvodnému príkladu a vytlačme si verziu Java. Teraz však presmerujeme výstup na súbor protokolu namiesto štandardného výstupného potrubia:
ProcessBuilder processBuilder = nový ProcessBuilder ("java", "-version"); processBuilder.redirectErrorStream (true); Protokol súboru = folder.newFile ("java-version.log"); processBuilder.redirectOutput (log); Proces proces = processBuilder.start ();
Vo vyššie uvedenom príklade vytvoríme nový dočasný súbor s názvom log a povieme náš ProcessBuilder presmerovať výstup na tento cieľ súboru.
V tomto poslednom úryvku to jednoducho skontrolujeme getInputStream () je skutočne nulový a že obsah nášho súboru je podľa očakávania:
assertEquals ("Ak je presmerovaný, mal by byť -1", -1, process.getInputStream (). read ()); Zoznam riadkov = Files.lines (log.toPath ()). Collect (Collectors.toList ()); assertThat ("Výsledky by mali obsahovať java verziu:", lines, hasItem (containsString ("java verzia")));
Teraz sa pozrime na miernu variáciu na tomto príklade. Napríklad keď chceme namiesto súboru zakaždým pridať nový k súboru protokolu:
Protokol súboru = tempFolder.newFile ("java-version-append.log"); processBuilder.redirectErrorStream (true); processBuilder.redirectOutput (Redirect.appendTo (log));
Je tiež dôležité spomenúť výzvu na číslo redirectErrorStream (true). V prípade akýchkoľvek chýb bude chybový výstup zlúčený do normálneho výstupného súboru procesu.
Môžeme samozrejme určiť jednotlivé súbory pre štandardný výstup a štandardný chybový výstup:
Súbor outputLog = tempFolder.newFile ("standard-output.log"); Súbor errorLog = tempFolder.newFile ("error.log"); processBuilder.redirectOutput (Redirect.appendTo (outputLog)); processBuilder.redirectError (Redirect.appendTo (errorLog));
3.5. Dedenie I / O súčasného procesu
V tomto predposlednom príklade uvidíme inheritIO () metóda v akcii. Túto metódu môžeme použiť, keď chceme presmerovať I / O podprocesov na štandardné I / O aktuálneho procesu:
@Test public void givenProcessBuilder_whenInheritIO_thenSuccess () vyvolá IOException, InterruptedException {ProcessBuilder processBuilder = nový ProcessBuilder ("/ bin / sh", "-c", "echo ahoj"); processBuilder.inheritIO (); Proces proces = processBuilder.start (); int exitCode = process.waitFor (); assertEquals ("Nemali by sa zistiť žiadne chyby", 0, exitCode); }
Vo vyššie uvedenom príklade pomocou inheritIO () metóda vidíme výstup jednoduchého príkazu v konzole v našom IDE.
V nasledujúcej časti sa pozrieme na to, aké doplnky boli urobené ProcessBuilder API v Jave 9.
4. Doplnky Java 9
Java 9 predstavila koncept potrubí do ProcessBuilder API:
verejný statický zoznam startPipeline (tvorcovia zoznamov)
Pomocou startPipeline metódou môžeme odovzdať zoznam ProcessBuilder predmety. Táto statická metóda potom spustí a Proces pre každý ProcessBuilder. Teda vytvorenie reťazca procesov, ktoré sú prepojené ich štandardným výstupom a štandardnými vstupnými tokmi.
Napríklad, ak chceme spustiť niečo také:
Nájsť . -name * .java -typ f | wc -l
To, čo by sme urobili, je vytvoriť nástroj na tvorbu procesov pre každý izolovaný príkaz a zostaviť ich do potrubia:
@Test public void givenProcessBuilder_whenStartingPipeline_thenSuccess () vyvolá IOException, InterruptedException {List builders = Arrays.asList (new ProcessBuilder ("find", "src", "-name", "* .java", "-type", "f") , nový ProcessBuilder ("wc", "-l")); Zoznam procesov = ProcessBuilder.startPipeline (stavitelia); Posledný proces = procesy.get (procesy.size () - 1); Zoznamový výstup = readOutput (last.getInputStream ()); assertThat ("Výsledky by nemali byť prázdne", výstup, je (nie (prázdny ()))); }
V tomto príklade hľadáme všetky súbory Java vo vnútri súboru src adresár a výsledky vložením do iného procesu spočítať.
Ak sa chcete dozvedieť viac o ďalších vylepšeniach vykonaných v rozhraní Process API v prostredí Java 9, prečítajte si náš skvelý článok o vylepšeniach rozhrania Java 9 Process API.
5. Záver
Aby sme to zhrnuli, v tomto tutoriáli sme preskúmali java.lang.ProcessBuilder API podrobne.
Najprv sme začali vysvetlením, čo sa dá s API urobiť, a zhrnuli sme najdôležitejšie metódy.
Ďalej sme sa pozreli na niekoľko praktických príkladov. Nakoniec sme sa pozreli na to, aké nové prírastky boli do API v Jave 9 zavedené.
Celý zdrojový kód článku je ako vždy k dispozícii na GitHub.