Shell-parancsok végrehajtása Javában
On október 23, 2021 by adminBevezetés
Ebben a cikkben megnézzük, hogyan használhatjuk a Runtime
és ProcessBuilder
osztályokat shell-parancsok és szkriptek futtatására Javával.
A számítógépeket sok minden automatizálására használjuk a napi munkánk során. A rendszergazdák állandóan sok parancsot futtatnak, amelyek közül néhány nagyon ismétlődő, és minimális változtatásokat igényel a futtatások között.
Ez a folyamat is megérett az automatizálásra. Nincs szükség arra, hogy mindent kézzel futtassunk. A Java segítségével egyetlen vagy több shell parancsot futtathatunk, shell szkripteket futtathatunk, a terminál/parancssorozatot futtathatjuk, munkakönyvtárakat állíthatunk be és környezeti változókat manipulálhatunk a core osztályokon keresztül.
Runtime.exec()
A Runtime
osztály a Java-ban egy magas szintű osztály, amely minden egyes Java alkalmazásban jelen van. Ezen keresztül maga az alkalmazás kommunikál azzal a környezettel, amelyben van.
Az alkalmazásunkhoz tartozó futási időt a getRuntime()
metóduson keresztül kivonva, a exec()
metódus segítségével közvetlenül tudunk parancsokat végrehajtani vagy .bat
/.sh
fájlokat futtatni.
A exec()
módszer néhány túlterhelt változatot kínál:
-
public Process exec(String command)
– Acommand
-ben szereplő parancsot hajtja végre egy külön folyamatban. -
public Process exec(String command, String envp)
– Végrehajtja acommand
-t, a környezeti változók tömbjével. Ezeket Strings tömbként adja meg, aname=value
formátumot követve. -
public Process exec(String command, String envp, File dir)
– Végrehajtja acommand
parancsot, a megadott környezeti változókkal, adir
könyvtárból. -
public Process exec(String cmdArray)
– Végrehajtja a parancsot egy Strings tömb formájában. -
public Process exec(String cmdArray, String envp)
– Végrehajtja a parancsot a megadott környezeti változókkal. -
public Process exec(String cmdarray, String envp, File dir)
– Parancsot hajt végre a megadott környezeti változókkal adir
könyvtárból.
Érdemes megjegyezni, hogy ezek a folyamatok az értelmezőn kívül futnak, és rendszerfüggőek lesznek.
Azt is érdemes megjegyezni, hogy a String command
és a String cmdArray
között van különbség. Ugyanazt a dolgot érik el. Egy command
amúgy is tömbre bomlik, így e kettő bármelyikének használata ugyanazt az eredményt fogja eredményezni.
Az már rajtad múlik, hogy a exec("dir /folder")
vagy a exec(new String{"dir", "/folder"}
az, amit használni szeretnél.
Írjunk fel néhány példát, hogy lássuk, miben különböznek ezek a túlterhelt módszerek egymástól.
Parancs végrehajtása stringből
Kezdjük a legegyszerűbb megközelítéssel a három közül:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
A kód futtatásával végrehajtjuk az általunk megadott parancsot string formátumban. Azonban nem látunk semmit, amikor ezt futtatjuk.
Az érvényesítéshez, hogy ez helyesen futott-e, a process
objektumot kell megszereznünk. Használjunk egy BufferedReader
-et, hogy megnézzük, mi történik:
public static void printResults(Process process) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = ""; while ((line = reader.readLine()) != null) { System.out.println(line); }}
Most, amikor ezt a metódust a exec()
metódus után futtatjuk, valami olyasmit kell eredményeznie, mint:
Pinging www.stackabuse.com with 32 bytes of data:Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Ping statistics for 104.18.57.23: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate round trip times in milli-seconds: Minimum = 21ms, Maximum = 21ms, Average = 21ms
Ne feledjük, hogy a folyamatinformációkat a Process
példányokból kell majd kinyernünk, ahogy más példákon keresztül haladunk.
Munkakönyvtár megadása
Ha egy parancsot mondjuk egy bizonyos mappából szeretnénk futtatni, akkor valami olyasmit teszünk, mint:
Process process = Runtime.getRuntime() .exec("cmd /c dir", null, new File("C:\Users\")); //.exec("sh -c ls", null, new File("Pathname")); for non-Windows usersprintResults(process);
Itt a exec()
metódust egy command
, egy null
az új környezeti változóknak és egy new File()
, amit a munkakönyvtárunknak állítunk be.
Az cmd /c
hozzáadása egy olyan parancs előtt, mint a dir
, érdemes megjegyezni.
Mivel Windowson dolgozom, ez megnyitja a cmd
és a /c
végrehajtja a következő parancsot. Ebben az esetben ez a dir
.
Az okot, hogy ez miért nem volt kötelező a ping
példánál, de ennél a példánál kötelező, egy SO felhasználó szépen megválaszolta.
Az előző kódrészlet futtatása a következőt eredményezi:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,555,214,848 bytes free
Nézzük meg, hogy az előző parancsot egyetlen karakterlánc helyett több különálló részben is megadhatjuk:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
A kódrészlet futtatása szintén a következőt eredményezi:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,542,808,064 bytes free
Végeredményben a megközelítéstől függetlenül – egyetlen String vagy egy String tömb használata – a beírt parancsot mindig tömbre bontjuk, mielőtt a mögöttes logika feldolgozza.
Az, hogy melyiket szeretnénk használni, csak azon múlik, hogy melyiket találjuk olvashatóbbnak.
Környezeti változók használata
Nézzük meg, hogyan használhatjuk a környezeti változókat:
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
A String tömbön belül annyi környezeti változót adhatunk meg, amennyit csak szeretnénk. Itt éppen a var1
értékét nyomtattuk ki a echo
használatával.
A kód futtatásával:
value1
A .bat és .sh fájlok futtatása
Néha sokkal egyszerűbb mindent egy fájlba pakolni és azt a fájlt futtatni ahelyett, hogy mindent programozottan adnánk hozzá.
Az operációs rendszertől függően a .bat
vagy a .sh
fájlokat használjuk. Hozzunk létre egyet a következő tartalommal:
echo Hello World
Ezután használjuk ugyanazt a megközelítést, mint korábban:
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Ez megnyitja a parancssort, és futtatja a .bat
fájlt az általunk beállított munkakönyvtárban.
Ez a kód futtatása elég biztosan azt eredményezi:
Mivel az összes túlterhelt exec()
aláírásról gondoskodtunk, nézzük meg a ProcessBuilder
osztályt és azt, hogyan tudunk parancsokat végrehajtani a segítségével.
ProcessBuilder
ProcessBuilder
az a mögöttes mechanizmus, amely lefuttatja a parancsokat, amikor a Runtime.getRuntime().exec()
metódust használjuk:
/** * Executes the specified command and arguments in a separate process with * the specified environment and working directory. *...*/public Process exec(String cmdarray, String envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start();}
JavaDocs a Runtime
osztályhoz
Megnézzük, hogyan veszi a ProcessBuilder
a exec()
metódusból származó bemenetünket és futtatja a parancsot, és ezzel egy jó ötletet kapunk arra, hogyan használjuk azt is.
Elvesz egy String cmdarray
-at, és ez elég ahhoz, hogy futtassuk. Alternatívaként olyan opcionális argumentumokkal is elláthatjuk, mint a String envp
és a File dir
.
Vizsgáljuk meg ezeket a lehetőségeket.
ProcessBuilder: Parancs végrehajtása karakterláncokból
Ahelyett, hogy egyetlen karakterláncot tudnánk megadni, mint például a cmd /c dir
, ebben az esetben fel kell bontanunk. Ha például a korábbiakhoz hasonlóan a C:/Users
könyvtárban lévő fájlokat szeretnénk felsorolni, akkor:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
Az Process
tényleges végrehajtásához a start()
parancsot futtatjuk, és a visszaadott értéket egy Process
példányhoz rendeljük.
A kód futtatásával:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,517,294,080 bytes free
Ez a megközelítés azonban semmivel sem jobb, mint az előző. Ami hasznos a ProcessBuilder
osztályban, az az, hogy testre szabható. Programozottan is beállíthatunk dolgokat, nem csak parancsokon keresztül.
ProcessBuilder: A munkakönyvtár megadása
Ahelyett, hogy a parancson keresztül adnánk meg a munkakönyvtárat, állítsuk be programozottan:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Itt a munkakönyvtárat ugyanarra állítjuk be, mint korábban, de ezt a meghatározást kivettük magából a parancsból. Ezt a kódot futtatva ugyanazt az eredményt kapjuk, mint az előző példában.
ProcessBuilder: Környezeti változók
A ProcessBuilder
módszereit használva könnyen lekérdezhetjük a környezeti változók listáját egy Map
formájában. A környezeti változókat is könnyen beállíthatjuk, hogy a programunk használni tudja őket.
Kérjük le a jelenleg rendelkezésre álló környezeti változókat, majd adjunk hozzá néhányat későbbi használatra:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Itt a visszakapott környezeti változókat egy Map
-be csomagoltuk, és egy forEach()
-t futtattunk rajta, hogy kiírjuk az értékeket a konzolunkra.
Ezt a kódot futtatva megkapjuk a gépünkön lévő környezeti változók listáját:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Most, adjunk hozzá egy környezeti változót ehhez a listához, és használjuk fel:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
Ezt a kódot futtatva megkapjuk:
value1
A program futásának befejezése után természetesen ez a változó nem marad a listában.
ProcessBuilder: .bat és .sh fájlok futtatása
Ha egy fájlt szeretnénk futtatni, ismét csak a ProcessBuilder
példányt látjuk el a szükséges információkkal:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
A kód futtatásának eredményeképpen megnyílik a parancssor és végrehajtja a .bat
fájlt:
Következtetés
Ebben a cikkben a héjparancsok Java nyelven történő futtatásának példáit vizsgáltuk. Ehhez a Runtime
és ProcessBuilder
osztályokat használtuk.
A Java segítségével egyetlen vagy több shell parancsot futtathatunk, shell szkripteket futtathatunk, a terminál/parancssorozatot futtathatjuk, munkakönyvtárakat állíthatunk be és környezeti változókat manipulálhatunk a core osztályok segítségével.
Vélemény, hozzászólás?