Executarea comenzilor shell în Java
On octombrie 23, 2021 by adminIntroducere
În acest articol, vom vedea cum putem folosi clasele Runtime
și ProcessBuilder
pentru a executa comenzi și scripturi shell cu Java.
Utilizăm computerele pentru a automatiza multe lucruri în munca noastră zilnică. Administratorii de sistem execută tot timpul multe comenzi, dintre care unele sunt foarte repetitive și necesită modificări minime între două execuții.
Acest proces este, de asemenea, pregătit pentru automatizare. Nu este nevoie să rulați totul manual. Utilizând Java, putem rula comenzi shell unice sau multiple, executa scripturi shell, rula terminalul/comandă prompt, seta directoare de lucru și manipula variabilele de mediu prin intermediul claselor de bază.
Runtime.exec()
Clasa Runtime
din Java este o clasă de nivel înalt, prezentă în fiecare aplicație Java. Prin intermediul ei, aplicația însăși comunică cu mediul în care se află.
Prin extragerea runtime-ului asociat aplicației noastre prin metoda getRuntime()
, putem folosi metoda exec()
pentru a executa direct comenzi sau pentru a rula fișiere .bat
/.sh
.
Metoda exec()
oferă câteva variante supraîncărcate:
-
public Process exec(String command)
– Execută comanda conținută încommand
într-un proces separat. -
public Process exec(String command, String envp)
– Executăcommand
, cu o matrice de variabile de mediu. Acestea sunt furnizate sub forma unui tablou de Strings, urmând formatulname=value
. -
public Process exec(String command, String envp, File dir)
– Execută comandacommand
, cu variabilele de mediu specificate, din interiorul directoruluidir
. -
public Process exec(String cmdArray)
– Execută o comandă sub forma unui tablou de Strings. -
public Process exec(String cmdArray, String envp)
– Execută o comandă cu variabilele de mediu specificate. -
public Process exec(String cmdarray, String envp, File dir)
– Execută o comandă, cu variabilele de mediu specificate, din interiorul directoruluidir
.
Este demn de remarcat faptul că aceste procese sunt rulate în exteriorul interpretorului și vor fi dependente de sistem.
De asemenea, este demn de remarcat diferența dintre String command
și String cmdArray
. Ele realizează același lucru. Un command
este oricum descompus într-un array, așa că folosirea oricăreia dintre aceste două ar trebui să dea aceleași rezultate.
Depinde de dumneavoastră să decideți dacă exec("dir /folder")
sau exec(new String{"dir", "/folder"}
este ceea ce doriți să folosiți.
Să scriem câteva exemple pentru a vedea cum diferă aceste metode supraîncărcate una de cealaltă.
Executarea unei comenzi din String
Să începem cu cea mai simplă abordare dintre aceste trei:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
Executarea acestui cod va executa comanda pe care am furnizat-o în format String. Cu toate acestea, nu vedem nimic atunci când executăm acest lucru.
Pentru a valida dacă acest lucru s-a executat corect, vom dori să punem mâna pe obiectul process
. Să folosim un BufferedReader
pentru a arunca o privire la ceea ce se întâmplă:
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); }}
Acum, când executăm această metodă după metoda exec()
, ar trebui să producă ceva de genul:
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
Rețineți că va trebui să extragem informațiile despre proces din instanțele Process
pe măsură ce parcurgem alte exemple.
Specificați directorul de lucru
Dacă doriți să executați o comandă din, să zicem, un anumit folder, vom face ceva de genul:
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);
Aici, am prevăzut metoda exec()
cu un command
, un null
pentru noile variabile de mediu și un new File()
care este setat ca fiind directorul nostru de lucru.
Este de remarcat adăugarea lui cmd /c
înaintea unei comenzi precum dir
.
Din moment ce lucrez pe Windows, aceasta deschide cmd
și /c
execută comanda ulterioară. În acest caz, este dir
.
Motivul pentru care acest lucru nu a fost obligatoriu pentru exemplul ping
, dar este obligatoriu pentru acest exemplu, a primit un răspuns frumos din partea unui utilizator SO.
Executarea bucății de cod anterioare va avea ca rezultat:
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
Să aruncăm o privire la modul în care am putea furniza comanda anterioară în mai multe părți individuale, în loc de un singur String:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
Executarea acestei bucăți de cod va avea ca rezultat ș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
:
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
În cele din urmă, indiferent de abordare – folosind un singur String sau un array de String-uri, comanda introdusă va fi întotdeauna împărțită într-un array înainte de a fi procesată de logica subiacentă.
Ce variantă doriți să folosiți se reduce doar la cea pe care o considerați mai ușor de citit.
Utilizarea variabilelor de mediu
Să vedem cum putem folosi variabilele de mediu:
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
Potem furniza oricâte variabile de mediu dorim în cadrul array-ului String. Aici, tocmai am imprimat valoarea lui var1
folosind echo
.
Executarea acestui cod va returna:
value1
Executarea fișierelor .bat și .sh
Câteodată, este mult mai ușor să descărcați totul într-un fișier și să executați acel fișier în loc să adăugați totul în mod programatic.
În funcție de sistemul dvs. de operare, veți folosi fie fișierele .bat
, fie .sh
. Să creăm unul cu conținutul:
echo Hello World
Apoi, să folosim aceeași abordare ca și înainte:
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Aceasta va deschide promptul de comandă și va rula fișierul .bat
în directorul de lucru pe care l-am stabilit.
Executând acest cod cu siguranță rezultă:
Cu toate semnăturile supraîncărcate exec()
rezolvate, să aruncăm o privire la clasa ProcessBuilder
și la modul în care putem executa comenzi cu ajutorul acesteia.
ProcessBuilder
ProcessBuilder
este mecanismul de bază care execută comenzile atunci când folosim metoda Runtime.getRuntime().exec()
:
/** * 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
Urmărind modul în care ProcessBuilder
preia datele de intrare din metoda exec()
și execută comanda, ne oferă o idee bună despre cum să o folosim, de asemenea.
Acesta acceptă un String cmdarray
, iar acest lucru este suficient pentru a o pune în funcțiune. Alternativ, îi putem furniza argumente opționale, cum ar fi String envp
și File dir
.
Să explorăm aceste opțiuni.
ProcessBuilder: Executarea comenzii din șiruri de caractere
În loc să putem furniza un singur șir de caractere, cum ar fi cmd /c dir
, va trebui să îl împărțim în acest caz. De exemplu, dacă am dori să enumerăm fișierele din directorul C:/Users
ca înainte, am face:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
Pentru a executa efectiv un Process
, executăm comanda start()
și atribuim valoarea returnată unei instanțe Process
.
Executarea acestui cod va produce:
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
Cu toate acestea, această abordare nu este cu nimic mai bună decât cea anterioară. Ceea ce este util cu clasa ProcessBuilder
este faptul că este personalizabilă. Putem seta lucrurile în mod programatic, nu doar prin comenzi.
ProcessBuilder: Specificarea directorului de lucru
În loc să furnizăm directorul de lucru prin intermediul comenzii, haideți să îl stabilim programatic:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Aici, am stabilit directorul de lucru să fie același ca înainte, dar am mutat această definiție în afara comenzii propriu-zise. Rularea acestui cod va oferi același rezultat ca și în ultimul exemplu.
ProcessBuilder: Variabile de mediu
Utilizând metodele ProcessBuilder
s, este ușor de recuperat o listă de variabile de mediu sub forma unui Map
. De asemenea, este ușor să setați variabilele de mediu astfel încât programul dvs. să le poată utiliza.
Să obținem variabilele de mediu disponibile în prezent și apoi să adăugăm unele pentru utilizare ulterioară:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Aici, am împachetat variabilele de mediu returnate într-un Map
și am rulat un forEach()
pe acesta pentru a imprima valorile în consola noastră.
Executarea acestui cod va produce o listă cu variabilele de mediu pe care le aveți pe mașina dumneavoastră:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Acum, haideți să adăugăm o variabilă de mediu la această listă și să o folosim:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
Executarea acestui cod va produce:
value1
Desigur, odată ce programul a terminat de rulat, această variabilă nu va rămâne în listă.
ProcessBuilder: Rularea fișierelor .bat și .sh
Dacă doriți să rulați un fișier, din nou, trebuie doar să furnizăm instanței ProcessBuilder
informațiile necesare:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
Rularea acestui cod are ca rezultat deschiderea promptului de comandă și executarea fișierului .bat
:
Concluzie
În acest articol, am explorat exemple de rulare a comenzilor shell în Java. Am folosit clasele Runtime
și ProcessBuilder
pentru a face acest lucru.
Utilizând Java, putem rula comenzi shell unice sau multiple, executa scripturi shell, rula promptul terminalului/comandă, seta directoare de lucru și manipula variabilele de mediu prin intermediul claselor de bază.
.
Lasă un răspuns