Exécution de commandes shell en Java
On octobre 23, 2021 by adminIntroduction
Dans cet article, nous allons examiner comment nous pouvons tirer parti des classes Runtime
et ProcessBuilder
pour exécuter des commandes et des scripts shell avec Java.
Nous utilisons les ordinateurs pour automatiser de nombreuses choses dans nos tâches quotidiennes. Les administrateurs système exécutent de nombreuses commandes tout le temps, dont certaines sont très répétitives et nécessitent des changements minimes entre les exécutions.
Ce processus est également mûr pour l’automatisation. Il n’est pas nécessaire de tout exécuter manuellement. En utilisant Java, nous pouvons exécuter des commandes shell uniques ou multiples, exécuter des scripts shell, exécuter le terminal/interrogation de commande, définir des répertoires de travail et manipuler des variables d’environnement par le biais de classes de base.
Runtime.exec()
La classe Runtime
en Java est une classe de haut niveau, présente dans chaque application Java unique. Par son intermédiaire, l’application elle-même communique avec l’environnement dans lequel elle se trouve.
En extrayant le runtime associé à notre application via la méthode getRuntime()
, nous pouvons utiliser la méthode exec()
pour exécuter directement des commandes ou exécuter des fichiers .bat
/.sh
.
La méthode exec()
offre quelques variations surchargées :
-
public Process exec(String command)
– Exécute la commande contenue danscommand
dans un processus séparé. -
public Process exec(String command, String envp)
– Exécute lecommand
, avec un tableau de variables d’environnement. Elles sont fournies sous la forme d’un tableau de Strings, suivant le formatname=value
. -
public Process exec(String command, String envp, File dir)
– Exécute lecommand
, avec les variables d’environnement spécifiées, à partir du répertoiredir
. -
public Process exec(String cmdArray)
– Exécute une commande sous la forme d’un tableau de Strings. -
public Process exec(String cmdArray, String envp)
– Exécute une commande avec les variables d’environnement spécifiées. -
public Process exec(String cmdarray, String envp, File dir)
– Exécute une commande, avec les variables d’environnement spécifiées, à partir du répertoiredir
.
Il convient de noter que ces processus sont exécutés en dehors de l’interpréteur et seront dépendants du système.
Ce qui est également à noter est la différence entre String command
et String cmdArray
. Ils réalisent la même chose. Un command
est décomposé en un tableau de toute façon, donc l’utilisation de l’un de ces deux devrait donner les mêmes résultats.
C’est à vous de décider si exec("dir /folder")
ou exec(new String{"dir", "/folder"}
est ce que vous souhaitez utiliser.
Ecrivons quelques exemples pour voir comment ces méthodes surchargées diffèrent les unes des autres.
Exécution d’une commande à partir de String
Débutons par l’approche la plus simple parmi ces trois :
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
L’exécution de ce code exécutera la commande que nous avons fournie au format String. Cependant, nous ne voyons rien lorsque nous exécutons ceci.
Pour valider si cela a été exécuté correctement, nous voudrons mettre la main sur l’objet process
. Utilisons un BufferedReader
pour jeter un coup d’œil à ce qui se passe:
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); }}
Maintenant, lorsque nous exécutons cette méthode après la méthode exec()
, elle devrait donner quelque chose du genre:
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
Gardez à l’esprit que nous devrons extraire les informations de processus des instances Process
au fur et à mesure des autres exemples.
Spécifier le répertoire de travail
Si vous souhaitez exécuter une commande à partir, disons, d’un certain dossier, nous ferions quelque chose du genre :
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);
Ici, nous avons fourni à la méthode exec()
un command
, un null
pour les nouvelles variables d’environnement et un new File()
qui est défini comme notre répertoire de travail.
L’ajout de cmd /c
avant une commande telle que dir
mérite d’être noté.
Comme je travaille sous Windows, cela ouvre le cmd
et /c
exécute la commande suivante. Dans ce cas, c’est dir
.
La raison pour laquelle cela n’était pas obligatoire pour l’exemple ping
, mais l’est pour cet exemple est joliment répondu par un utilisateur de SO.
L’exécution du morceau de code précédent aboutira à :
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
Regardons comment nous pourrions fournir la commande précédente en plusieurs parties individuelles, au lieu d’une seule Chaîne :
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
L’exécution de ce morceau de code aboutira également à :
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
En définitive, quelle que soit l’approche – utilisation d’un seul String ou d’un tableau de String, la commande que vous saisissez sera toujours décomposée en un tableau avant d’être traitée par la logique sous-jacente.
Celle que vous aimeriez utiliser se résume juste à celle que vous trouvez plus lisible.
Utilisation des variables d’environnement
Regardons comment nous pouvons utiliser les variables d’environnement :
Process process = Runtime.getRuntime().exec( "cmd /c echo %var1%", new String{"var1=value1"}); printResults(process);
Nous pouvons fournir autant de variables d’environnement que nous le souhaitons dans le tableau String. Ici, nous venons d’imprimer la valeur de var1
en utilisant echo
.
L’exécution de ce code retournera :
value1
Exécution des fichiers .bat et .sh
Parfois, il est juste beaucoup plus facile de tout décharger dans un fichier et d’exécuter ce fichier au lieu de tout ajouter par programme.
Selon votre système d’exploitation, vous utiliserez soit des fichiers .bat
, soit des fichiers .sh
. Créons-en un avec le contenu :
echo Hello World
Puis, utilisons la même approche que précédemment :
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
Cela va ouvrir l’invite de commande et exécuter le fichier .bat
dans le répertoire de travail que nous avons défini.
L’exécution de ce code aboutit assez sûrement à :
Avec toutes les signatures surchargées exec()
prises en charge, jetons un coup d’œil à la classe ProcessBuilder
et à la façon dont nous pouvons exécuter des commandes en l’utilisant.
ProcessBuilder
ProcessBuilder
est le mécanisme sous-jacent qui exécute les commandes lorsque nous utilisons la méthode 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 pour la classe Runtime
Prendre connaissance de la façon dont la ProcessBuilder
prend notre entrée de la méthode exec()
et exécute la commande, nous donne une bonne idée de la façon de l’utiliser également.
Il accepte un String cmdarray
, et c’est suffisant pour le faire fonctionner. Alternativement, nous pouvons lui fournir des arguments optionnels tels que les String envp
et File dir
.
Explorons ces options.
ProcessBuilder : Exécution de la commande à partir de chaînes de caractères
Au lieu de pouvoir fournir une seule chaîne de caractères, telle que cmd /c dir
, nous devrons la décomposer dans ce cas. Par exemple, si nous voulions lister les fichiers du répertoire C:/Users
comme avant, nous ferions :
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
Pour exécuter réellement une Process
, nous exécutons la commande start()
et assignons la valeur retournée à une instance Process
.
L’exécution de ce code donnera :
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
Mais cette approche n’est pas meilleure que la précédente. Ce qui est utile avec la classe ProcessBuilder
, c’est qu’elle est personnalisable. Nous pouvons définir des choses par programme, et pas seulement via des commandes.
ProcessBuilder : Spécifier le répertoire de travail
Au lieu de fournir le répertoire de travail via la commande, définissons-le de manière programmatique:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
Ici, nous avons défini le répertoire de travail comme étant le même que précédemment, mais nous avons déplacé cette définition hors de la commande elle-même. L’exécution de ce code donnera le même résultat que le dernier exemple.
ProcessBuilder : Variables d’environnement
En utilisant les méthodes de ProcessBuilder
s, il est facile de récupérer une liste de variables d’environnement sous la forme d’un Map
. Il est également facile de définir les variables d’environnement afin que votre programme puisse les utiliser.
Recevons les variables d’environnement actuellement disponibles et ajoutons-en quelques-unes pour une utilisation ultérieure :
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
Ici, nous avons emballé les variables d’environnement retournées dans un Map
et exécuté un forEach()
dessus pour imprimer les valeurs sur notre console.
L’exécution de ce code donnera une liste des variables d’environnement que vous avez sur votre machine :
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
Maintenant, ajoutons une variable d’environnement à cette liste et utilisons-la :
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
L’exécution de ce code donnera :
value1
Bien sûr, une fois que le programme aura fini de s’exécuter, cette variable ne restera pas dans la liste.
ProcessBuilder : Exécution des fichiers .bat et .sh
Si vous souhaitez exécuter un fichier, là encore, il suffit de fournir à l’instance ProcessBuilder
les informations requises:
processBuilder .command("cmd", "/c", "start", "file.bat") .directory(new File("C:\Users\User\Desktop"));Process process = processBuilder.start();
L’exécution de ce code entraîne l’ouverture de l’invite de commande et l’exécution du fichier .bat
:
Conclusion
Dans cet article, nous avons exploré des exemples d’exécution de commandes shell en Java. Nous avons utilisé les classes Runtime
et ProcessBuilder
pour ce faire.
En utilisant Java, nous pouvons exécuter des commandes shell uniques ou multiples, exécuter des scripts shell, exécuter le terminal/interrogation de commande, définir des répertoires de travail et manipuler des variables d’environnement à travers les classes de base.
Laisser un commentaire