Utföra skalkommandon i Java
On oktober 23, 2021 by adminInledning
I den här artikeln tittar vi på hur vi kan utnyttja klasserna Runtime
och ProcessBuilder
för att utföra skalkommandon och skript med Java.
Vi använder datorer för att automatisera många saker i vårt dagliga arbete. Systemadministratörer kör många kommandon hela tiden, varav vissa är mycket repetitiva och kräver minimala ändringar mellan körningarna.
Denna process är också mogen för automatisering. Det finns ingen anledning att köra allting manuellt. Med hjälp av Java kan vi köra enstaka eller flera skalkommandon, exekvera skalskript, köra terminalen/kommandoprompten, ställa in arbetskataloger och manipulera miljövariabler genom kärnklasser.
Runtime.exec()
Klassen Runtime
i Java är en klass på hög nivå som finns i varje enskild Java-applikation. Genom den kommunicerar själva applikationen med den miljö den befinner sig i.
Då vi extraherar den körtid som är associerad med vår applikation via getRuntime()
-metoden kan vi använda exec()
-metoden för att utföra kommandon direkt eller köra .bat
/.sh
-filer.
Metoden exec()
erbjuder några överladdade varianter:
-
public Process exec(String command)
– Exekverar kommandot som finns icommand
i en separat process. -
public Process exec(String command, String envp)
– Exekverarcommand
, med en array av miljövariabler. De tillhandahålls som en array av strängar, enligt formatetname=value
. -
public Process exec(String command, String envp, File dir)
– Exekverarcommand
, med de angivna miljövariablerna, från katalogendir
. -
public Process exec(String cmdArray)
– Exekverar ett kommando i form av en array av strängar. -
public Process exec(String cmdArray, String envp)
– Exekverar ett kommando med de angivna miljövariablerna. -
public Process exec(String cmdarray, String envp, File dir)
– Utför ett kommando med de angivna miljövariablerna från katalogendir
.
Det är värt att notera att dessa processer körs externt från tolken och kommer att vara systemberoende.
Vad som också är värt att notera är skillnaden mellan String command
och String cmdArray
. De uppnår samma sak. En command
delas ändå upp i en array, så att använda någon av dessa två bör ge samma resultat.
Det är upp till dig att avgöra om exec("dir /folder")
eller exec(new String{"dir", "/folder"}
är det du vill använda.
Låt oss skriva några exempel för att se hur dessa överladdade metoder skiljer sig från varandra.
Exekvera ett kommando från String
Låt oss börja med det enklaste tillvägagångssättet av dessa tre:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
Att köra den här koden kommer att exekvera kommandot vi har angett i String-format. Vi ser dock ingenting när vi kör detta.
För att bekräfta om detta kördes korrekt vill vi få tag på process
-objektet. Låt oss använda en BufferedReader
för att ta en titt på vad som händer:
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); }}
När vi nu kör den här metoden efter exec()
-metoden bör den ge något i stil med:
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
Håll i minnet att vi måste extrahera processinformationen från Process
-instanserna när vi går igenom andra exempel.
Ange arbetskatalogen
Om du vill köra ett kommando från till exempel en viss mapp gör vi något i stil med:
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);
Här har vi försett exec()
-metoden med en command
, en null
för nya miljövariabler och en new File()
som är satt som vår arbetskatalog.
Har man lagt till cmd /c
före ett kommando som dir
är det värt att notera.
Då jag arbetar i Windows öppnar detta cmd
och /c
utför det efterföljande kommandot. I det här fallet är det dir
.
Anledningen till att detta inte var obligatoriskt för ping
-exemplet, men är obligatoriskt för det här exemplet besvaras snyggt av en SO-användare.
Att köra föregående kodstycke kommer att resultera i:
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
Vi kan ta en titt på hur vi skulle kunna leverera föregående kommando i flera enskilda delar, istället för en enda sträng:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
Att köra detta kodstycke kommer också att resultera i:
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
Oavsett vilket tillvägagångssätt som används – en enskild sträng eller en strängarray – kommer kommandot du matar in alltid att delas upp i en array innan det bearbetas av den underliggande logiken.
Vilken du vill använda kokar bara ner till vilken du tycker är mer läsbar.
Användning av miljövariabler
Låt oss ta en titt på hur vi kan använda miljövariabler:
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
Vi kan tillhandahålla så många miljövariabler som vi vill inom String arrayen. Här har vi just skrivit ut värdet på var1
med hjälp av echo
.
Körning av den här koden ger följande resultat:
value1
Körning av .bat- och .sh-filer
Under vissa omständigheter är det mycket enklare att avlasta allting till en fil och köra den filen i stället för att lägga till allting programmässigt.
Avhängigt av ditt operativsystem använder du antingen .bat
eller .sh
-filer. Låt oss skapa en sådan med följande innehåll:
echo Hello World
Därefter använder vi samma tillvägagångssätt som tidigare:
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Detta öppnar kommandotolken och kör .bat
-filen i arbetskatalogen som vi har ställt in.
Att köra den här koden resulterar säkert nog i:
Med alla överbelastade exec()
-signaturer omhändertagna, låt oss ta en titt på ProcessBuilder
-klassen och hur vi kan exekvera kommandon med hjälp av den.
ProcessBuilder
ProcessBuilder
är den underliggande mekanismen som kör kommandona när vi använder Runtime.getRuntime().exec()
-metoden:
/** * 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 for the Runtime
class
Om vi tar en titt på hur ProcessBuilder
tar emot vår inmatning från exec()
-metoden och kör kommandot, får vi en bra idé om hur den också kan användas.
Den tar emot en String cmdarray
, och det räcker för att få igång den. Alternativt kan vi förse den med valfria argument som String envp
och File dir
.
Låt oss utforska dessa alternativ.
ProcessBuilder: I stället för att kunna tillhandahålla en enda sträng, till exempel cmd /c dir
, måste vi dela upp den i det här fallet. Om vi till exempel vill lista filerna i katalogen C:/Users
som tidigare skulle vi göra:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
För att faktiskt exekvera en Process
kör vi kommandot start()
och tilldelar det returnerade värdet till en Process
-instans.
Körning av den här koden kommer att ge:
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
Det här tillvägagångssättet är dock inte bättre än det föregående. Det som är användbart med ProcessBuilder
-klassen är att den är anpassningsbar. Vi kan ställa in saker programmatiskt, inte bara via kommandon.
ProcessBuilder:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Istället för att ange arbetskatalogen via kommandot kan vi ställa in den programmatiskt:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Här har vi ställt in arbetskatalogen på samma sätt som tidigare, men vi har flyttat den definitionen ut ur själva kommandot. Om du kör den här koden får du samma resultat som i det senaste exemplet.
ProcessBuilder: Environment Variables
Med hjälp av ProcessBuilder
s metoder är det enkelt att hämta en lista över miljövariabler i form av en Map
. Det är också enkelt att ställa in miljövariabler så att ditt program kan använda dem.
Låt oss hämta de miljövariabler som för närvarande är tillgängliga och sedan lägga till några för senare användning:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Här har vi packat de returnerade miljövariablerna i en Map
och kört en forEach()
på den för att skriva ut värdena till vår konsol.
Körning av den här koden ger en lista över de miljövariabler du har på din maskin:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Nu lägger vi till en miljövariabel i listan och använder den:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
Körning av den här koden ger:
value1
När programmet väl är färdigt att köras kommer den här variabeln förstås inte att finnas kvar i listan.
ProcessBuilder: Om du vill köra en fil är det återigen bara att förse ProcessBuilder
-instansen med nödvändig information:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
Körningen av den här koden resulterar i att kommandotolken öppnas och att .bat
-filen exekveras:
Slutsats
I den här artikeln har vi undersökt exempel på hur man kan köra skalkommandon i Java. Vi har använt klasserna Runtime
och ProcessBuilder
för att göra detta.
Med hjälp av Java kan vi köra enstaka eller flera skalkommandon, exekvera skalskript, köra terminalen/kommandoprompten, ställa in arbetskataloger och manipulera miljövariabler genom kärnklasser.
Lämna ett svar