Shell commando’s uitvoeren in Java
On oktober 23, 2021 by adminInleiding
In dit artikel bekijken we hoe we de Runtime
en ProcessBuilder
classes kunnen gebruiken om shell commando’s en scripts uit te voeren met Java.
We gebruiken computers om veel dingen in ons dagelijks werk te automatiseren. Systeembeheerders voeren voortdurend veel commando’s uit, waarvan sommige zeer repetitief zijn en tussen de runs door minimale veranderingen vereisen.
Dit proces is ook rijp voor automatisering. Het is niet nodig om alles handmatig uit te voeren. Met Java kunnen we enkele of meerdere shell commando’s uitvoeren, shell scripts uitvoeren, de terminal/command prompt starten, werkdirectories instellen en omgevingsvariabelen manipuleren via core classes.
Runtime.exec()
De Runtime
class in Java is een high-level class, die in elke Java applicatie aanwezig is. Via deze klasse communiceert de applicatie zelf met de omgeving waarin het zich bevindt.
Door de runtime te extraheren die aan onze applicatie is gekoppeld via de getRuntime()
methode, kunnen we de exec()
methode gebruiken om direct commando’s uit te voeren of .bat
/.sh
bestanden uit te voeren.
De exec()
methode biedt een paar overloaded variaties:
-
public Process exec(String command)
– Voert het commando uit dat incommand
in een apart proces staat. -
public Process exec(String command, String envp)
– Voert decommand
uit, met een array van omgevingsvariabelen. Ze worden geleverd als een array van Strings, volgens hetname=value
formaat. -
public Process exec(String command, String envp, File dir)
– Voert decommand
uit, met de gespecificeerde omgevingsvariabelen, vanuit dedir
directory. -
public Process exec(String cmdArray)
– Voert een commando uit in de vorm van een array van Strings. -
public Process exec(String cmdArray, String envp)
– Voert een commando uit met de gespecificeerde omgevingsvariabelen. -
public Process exec(String cmdarray, String envp, File dir)
– Voert een commando uit, met de gespecificeerde omgevingsvariabelen, vanuit dedir
directory.
Het is de moeite waard om op te merken dat deze processen extern van de interpreter worden uitgevoerd en systeemafhankelijk zullen zijn.
Wat ook de moeite waard is om op te merken, is het verschil tussen String command
en String cmdArray
. Ze bereiken hetzelfde. Een command
wordt toch al in een array opgedeeld, dus het gebruik van een van deze twee zou dezelfde resultaten moeten opleveren.
Het is aan u om te beslissen of exec("dir /folder")
of exec(new String{"dir", "/folder"}
is wat u wilt gebruiken.
Laten we een paar voorbeelden schrijven om te zien hoe deze overloaded methoden van elkaar verschillen.
Uitvoeren van een commando van String
Laten we beginnen met de eenvoudigste aanpak van deze drie:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
Het uitvoeren van deze code zal het commando uitvoeren dat we in String formaat hebben aangeleverd. We zien echter niets als we dit uitvoeren.
Om te valideren of dit correct is uitgevoerd, willen we het process
object te pakken krijgen. Laten we een BufferedReader
gebruiken om te kijken wat er gebeurt:
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); }}
Nu, als we deze methode uitvoeren na de exec()
methode, zou het iets moeten opleveren in de trant van:
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
Houd in gedachten dat we de proces informatie uit de Process
instanties moeten halen als we door andere voorbeelden gaan.
Specificeer de Werkdirectory
Als u een commando wilt uitvoeren vanuit, laten we zeggen, een bepaalde map, dan zouden we iets doen in de trant van:
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);
Hier hebben we de exec()
methode voorzien van een command
, een null
voor nieuwe omgevingsvariabelen en een new File()
die is ingesteld als onze werkdirectory.
De toevoeging van cmd /c
voor een commando zoals dir
is de moeite van het opmerken waard.
Omdat ik onder Windows werk, opent dit de cmd
en /c
voert het daaropvolgende commando uit. In dit geval is dat dir
.
De reden waarom dit niet verplicht was voor het ping
voorbeeld, maar wel voor dit voorbeeld, is mooi beantwoord door een SO gebruiker.
Het uitvoeren van het vorige stukje code zal resulteren in:
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
Laten we eens kijken hoe we het vorige commando in verschillende afzonderlijke delen zouden kunnen leveren, in plaats van een enkele String:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
Het uitvoeren van dit stukje code zal ook resulteren in:
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
Ultime, ongeacht de aanpak – met behulp van een enkele String of een String array, de opdracht die u invoert zal altijd worden opgesplitst in een array alvorens te worden verwerkt door onderliggende logica.
Welke u wilt gebruiken, komt gewoon neer op welke u leesbaarder vindt.
Omgevingsvariabelen gebruiken
Laten we eens kijken hoe we omgevingsvariabelen kunnen gebruiken:
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
We kunnen zo veel omgevingsvariabelen opgeven als we willen binnen de String-array. Hier hebben we zojuist de waarde van var1
afgedrukt met behulp van echo
.
Het uitvoeren van deze code levert het volgende op:
value1
Het uitvoeren van .bat en .sh bestanden
Soms is het gewoon veel gemakkelijker om alles in een bestand te laden en dat bestand uit te voeren in plaats van alles programmatisch toe te voegen.
Afhankelijk van uw besturingssysteem, zou u .bat
of .sh
bestanden gebruiken. Laten we er een maken met de inhoud:
echo Hello World
Dan, laten we dezelfde aanpak gebruiken als eerder:
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Dit zal de commando prompt openen en het .bat
bestand uitvoeren in de werkdirectory die we hebben ingesteld.
Het uitvoeren van deze code resulteert in:
Met alle overbelaste exec()
handtekeningen, laten we eens kijken naar de ProcessBuilder
klasse en hoe we commando’s kunnen uitvoeren met behulp daarvan.
ProcessBuilder
ProcessBuilder
is het onderliggende mechanisme dat de commando’s uitvoert wanneer we de Runtime.getRuntime().exec()
methode gebruiken:
/** * 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 voor de Runtime
klasse
Een blik op hoe de ProcessBuilder
onze invoer van de exec()
methode neemt en het commando uitvoert, geeft ons een goed idee van hoe we het ook kunnen gebruiken.
Het accepteert een String cmdarray
, en dat is genoeg om het aan de gang te krijgen. Als alternatief kunnen we het voorzien van optionele argumenten, zoals de String envp
en File dir
.
Laten we deze opties eens verkennen.
ProcessBuilder: Executing Command from Strings
In plaats van een enkele String op te kunnen geven, zoals cmd /c dir
, zullen we het in dit geval moeten opsplitsen. Als we bijvoorbeeld de bestanden in de C:/Users
directory willen zien, zoals voorheen, doen we:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
Om daadwerkelijk een Process
uit te voeren, voeren we het start()
commando uit en wijzen de geretourneerde waarde toe aan een Process
instantie.
Het uitvoeren van deze code levert op:
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
Deze aanpak is echter niet beter dan de vorige. Wat handig is met de ProcessBuilder
klasse is dat het aanpasbaar is. We kunnen dingen programmatisch instellen, niet alleen via commando’s.
ProcessBuilder: Specificeer de werkdirectory
In plaats van de werkdirectory via het commando op te geven, stellen we die programmatisch in:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Hier hebben we de werkdirectory op dezelfde als voorheen ingesteld, maar we hebben die definitie uit het commando zelf verplaatst. Het uitvoeren van deze code zal hetzelfde resultaat opleveren als het laatste voorbeeld.
ProcessBuilder: Omgevingsvariabelen
Met behulp van ProcessBuilder
s methoden is het eenvoudig om een lijst met omgevingsvariabelen op te vragen in de vorm van een Map
. Het is ook eenvoudig om omgevingsvariabelen in te stellen, zodat uw programma ze kan gebruiken.
Laten we de omgevingsvariabelen opvragen die op dit moment beschikbaar zijn en er dan een paar toevoegen voor later gebruik:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Hier hebben we de geretourneerde omgevingsvariabelen verpakt in een Map
en er een forEach()
op uitgevoerd om de waarden op onze console uit te printen.
Het uitvoeren van deze code levert een lijst op van de omgevingsvariabelen die u op uw machine hebt:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Nu, laten we een omgevingsvariabele aan die lijst toevoegen en hem gebruiken:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
Het uitvoeren van deze code levert op:
value1
Natuurlijk, als het programma klaar is met draaien, zal deze variabele niet in de lijst blijven.
ProcessBuilder: Running .bat and .sh Files
Als u een bestand wilt uitvoeren, kunt u de ProcessBuilder
instantie voorzien van de benodigde informatie:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
Het uitvoeren van deze code resulteert in het openen van de opdrachtprompt en het uitvoeren van het .bat
bestand:
Conclusie
In dit artikel hebben we voorbeelden bekeken van het uitvoeren van shell commando’s in Java. We hebben hiervoor de klassen Runtime
en ProcessBuilder
gebruikt.
Met Java kunnen we enkelvoudige of meervoudige shell-commando’s uitvoeren, shell-scripts uitvoeren, de terminal/command prompt starten, werkdirectories instellen en omgevingsvariabelen manipuleren via core klassen.
Geef een antwoord