Comment exécuter le script shell Unix à partir du code Java?

155

Il est assez simple d'exécuter une commande Unix depuis Java.

Runtime.getRuntime().exec(myCommand);

Mais est-il possible d'exécuter un script shell Unix à partir du code Java? Si oui, serait-ce une bonne pratique d'exécuter un script shell à partir du code Java?

Lii
la source
3
Les choses deviennent intéressantes si ce script shell est interactif.
ernesto
quelle est la variable myCommand est cette chaîne? si oui, alors cela ne fonctionnera pas, la méthode exec nécessite String [] et argument, voir ci-dessous mon answar, cela fonctionne parfaitement
Girdhar Singh Rathore

Réponses:

174

Vous devriez vraiment regarder Process Builder . Il est vraiment conçu pour ce genre de choses.

ProcessBuilder pb = new ProcessBuilder("myshellScript.sh", "myArg1", "myArg2");
 Map<String, String> env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 Process p = pb.start();
Milhous
la source
3
Est-ce une bonne pratique d'appeler des scripts depuis JAVA? Des problèmes de performances?
kautuksahni
1
Notez que vous devrez peut-être spécifier le programme / bin / bash ou sh pour exécuter le script en fonction de la configuration de Java (voir stackoverflow.com/questions/25647806/… )
Ben Holland
@Milhous Je sais que c'est assez tard et que les choses ont peut-être changé, mais selon la documentation actuelle du processus Java, cette méthode n'est pas recommandée pour les scripts shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Les méthodes qui créent des processus peuvent ne pas fonctionner correctement pour les processus spéciaux sur certaines plates-formes natives, telles que les processus de fenêtrage natifs, les processus démons, les processus Win16 / DOS sous Microsoft Windows ou les scripts shell. "
Harman
24

Vous pouvez également utiliser la bibliothèque exécutable Apache Commons .

Exemple :

package testShellScript;

import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;

public class TestScript {
    int iExitValue;
    String sCommandString;

    public void runScript(String command){
        sCommandString = command;
        CommandLine oCmdLine = CommandLine.parse(sCommandString);
        DefaultExecutor oDefaultExecutor = new DefaultExecutor();
        oDefaultExecutor.setExitValue(0);
        try {
            iExitValue = oDefaultExecutor.execute(oCmdLine);
        } catch (ExecuteException e) {
            System.err.println("Execution failed.");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("permission denied.");
            e.printStackTrace();
        }
    }

    public static void main(String args[]){
        TestScript testScript = new TestScript();
        testScript.runScript("sh /root/Desktop/testScript.sh");
    }
}

Pour plus d'informations, un exemple est également donné sur Apache Doc .

Pas un bug
la source
Puis-je l'exécuter sous Windows?
bekur le
@KisHanSarsecHaGajjar pouvons-nous également capturer la sortie de shellscript et l'afficher dans l'interface utilisateur java. Je veux savoir s'il est possible de le faire
Kranthi Sama
@KranthiSama Vous pouvez définir OutputStreampour l' DefaultExecuterutilisation DefaultExecuter.setStreamHandlerméthode pour la sortie de capture dans OutputStream. Veuillez vous référer à ce fil pour plus d'informations: Comment puis-je capturer la sortie d'une commande ...
Pas un bogue
1
Lien pour ajouter la dépendance bibliothèque Apache Commons Exec à votre projet - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead
2
la meilleure solution.
Dev
23

Je dirais que ce n'est pas dans l'esprit de Java d'exécuter un script shell à partir de Java. Java est censé être multiplateforme et l'exécution d'un script shell limiterait son utilisation à UNIX.

Cela dit, il est certainement possible d'exécuter un script shell depuis Java. Vous utiliseriez exactement la même syntaxe que celle que vous avez indiquée (je ne l'ai pas essayé moi-même, mais essayez d'exécuter le script shell directement, et si cela ne fonctionne pas, exécutez le shell lui-même, en passant le script en tant que paramètre de ligne de commande) .

Jack Leow
la source
43
Oui, mais à bien des égards, ce mantra «l'esprit de Java» ou «écrire une fois partout» est de toute façon un mythe.
BobbyShaftoe
15
Qu'est-ce que c'est mythique?
Chris Ballance
15
ce qui est mythique à ce sujet est que vous gravez généralement nombreuses switcheset ifdéclarations pour contourner toutes les nuances qui ne fonctionnent pas exactement les mêmes sur différentes plates - formes , malgré les efforts des personnes qui sont venues avec les bibliothèques de base de java.
Brian Sweeney
2
Etonnamment! Je suis d'accord avec tous les commentaires ci-dessus et la réponse!
AnBisw
11
@BobbyShaftoe J'écris en java depuis 16 ans, et j'ai toujours développé sur Windows, toutes mes applications toujours déployées sur des boîtiers unix à saveur solaris / ibm ou oracle, donc je n'ai aucune idée de ce dont vous parlez
Kalpesh Soni
21

Je pense que vous avez répondu à votre propre question avec

Runtime.getRuntime().exec(myShellScript);

Quant à savoir si c'est une bonne pratique ... qu'essayez-vous de faire avec un script shell que vous ne pouvez pas faire avec Java?

Chris Ballance
la source
J'ai fait face à une situation similaire où je dois rsync quelques fichiers sur différents serveurs lorsqu'une certaine condition se produit dans mon code java. Y a-t-il un autre meilleur moyen?
v kumar
1
@Chris Ballance ... Je sais que ce commentaire est presque au bout de 10 ans :) mais pour répondre à votre question, que se passe-t-il si mon programme doit interagir avec une demi-douzaine de canaux en aval et en amont et en fonction de leur mode de communication accepté. Surtout quand vous travaillez sur un projet qui interagit avec tant de canaux étranges :)
Stunner
Pousser la fonctionnalité vers un script shell serait un effort ultime s'il n'y a pas d'autre moyen de faire le travail en Java. Il sera nécessairement compliqué de coordonner l'état et les dépendances si vous transférez le travail vers un script shell. Parfois, un script shell est le seul moyen ou le calendrier en fait le seul moyen raisonnable d'accomplir un peu de travail, c'est donc un moyen de le faire.
Chris Ballance
11

Oui, il est possible de le faire. Cela a fonctionné pour moi.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.omg.CORBA.portable.InputStream;

public static void readBashScript() {
        try {
            Process proc = Runtime.getRuntime().exec("/home/destino/workspace/JavaProject/listing.sh /"); //Whatever you want to execute
            BufferedReader read = new BufferedReader(new InputStreamReader(
                    proc.getInputStream()));
            try {
                proc.waitFor();
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            while (read.ready()) {
                System.out.println(read.readLine());
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
Desta Haileselassie Hagos
la source
7

Voici mon exemple. J'espère que cela a du sens.

public static void excuteCommand(String filePath) throws IOException{
    File file = new File(filePath);
    if(!file.isFile()){
        throw new IllegalArgumentException("The file " + filePath + " does not exist");
    }
    if(isLinux()){
        Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", filePath}, null);
    }else if(isWindows()){
        Runtime.getRuntime().exec("cmd /c start " + filePath);
    }
}
public static boolean isLinux(){
    String os = System.getProperty("os.name");  
    return os.toLowerCase().indexOf("linux") >= 0;
}

public static boolean isWindows(){
    String os = System.getProperty("os.name");
    return os.toLowerCase().indexOf("windows") >= 0;
}
Lionel Yan
la source
5

Oui, c'est possible et vous y avez répondu! Concernant les bonnes pratiques, je pense qu'il est préférable de lancer des commandes à partir de fichiers et non directement à partir de votre code. Donc , vous devez faire exécuter Java la liste des commandes (ou une commande) dans un existant .bat, .sh, .kshfichiers .... Voici un exemple d'exécution d'une liste de commandes dans un fichier MyFile.sh:

    String[] cmd = { "sh", "MyFile.sh", "\pathOfTheFile"};
    Runtime.getRuntime().exec(cmd);
YANKEE
la source
4

Pour éviter d'avoir à coder en dur un chemin absolu, vous pouvez utiliser la méthode suivante qui trouvera et exécutera votre script s'il se trouve dans votre répertoire racine.

public static void runScript() throws IOException, InterruptedException {
    ProcessBuilder processBuilder = new ProcessBuilder("./nameOfScript.sh");
    //Sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.
    processBuilder.inheritIO();
    Process process = processBuilder.start();

    int exitValue = process.waitFor();
    if (exitValue != 0) {
        // check for errors
        new BufferedInputStream(process.getErrorStream());
        throw new RuntimeException("execution of script failed!");
    }
}
anataliocs
la source
3

Quant à moi, tout doit être simple. Pour exécuter le script, il suffit d'exécuter

new ProcessBuilder("pathToYourShellScript").start();
Vladimir Bosyi
la source
3

La bibliothèque ZT Process Executor est une alternative à Apache Commons Exec. Il a des fonctionnalités pour exécuter des commandes, capturer leur sortie, définir des délais d'expiration, etc.

Je ne l'ai pas encore utilisé, mais il semble raisonnablement bien documenté.

Un exemple tiré de la documentation: exécution d'une commande, pompage du stderr vers un enregistreur, retour de la sortie sous forme de chaîne UTF8.

 String output = new ProcessExecutor().command("java", "-version")
    .redirectError(Slf4jStream.of(getClass()).asInfo())
    .readOutput(true).execute()
    .outputUTF8();

Sa documentation répertorie les avantages suivants par rapport à Commons Exec:

  • Gestion améliorée des flux
    • Lecture / écriture dans les flux
    • Rediriger stderr vers stdout
  • Gestion améliorée des délais d'expiration
  • Contrôle amélioré des codes de sortie
  • API améliorée
    • One liners pour des cas d'utilisation assez complexes
    • Une doublure pour obtenir la sortie du processus dans une chaîne
    • Accès à l' objet Process disponible
    • Prise en charge des processus asynchrones ( futur )
  • Journalisation améliorée avec l' API SLF4J
  • Prise en charge de plusieurs processus
Lii
la source
2

Voici un exemple comment exécuter un script Unix bash ou Windows bat / cmd à partir de Java. Les arguments peuvent être passés sur le script et la sortie reçue du script. La méthode accepte un nombre arbitraire d'arguments.

public static void runScript(String path, String... args) {
    try {
        String[] cmd = new String[args.length + 1];
        cmd[0] = path;
        int count = 0;
        for (String s : args) {
            cmd[++count] = args[count - 1];
        }
        Process process = Runtime.getRuntime().exec(cmd);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        try {
            process.waitFor();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        while (bufferedReader.ready()) {
            System.out.println("Received from script: " + bufferedReader.readLine());
        }
    } catch (Exception ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
    }
}

Lors de l'exécution sous Unix / Linux, le chemin doit être de type Unix (avec '/' comme séparateur), lors de l'exécution sous Windows - utilisez '\'. Hier est un exemple de script bash (test.sh) qui reçoit un nombre arbitraire d'arguments et double chaque argument:

#!/bin/bash
counter=0
while [ $# -gt 0 ]
do
  echo argument $((counter +=1)): $1
  echo doubling argument $((counter)): $(($1+$1))
  shift
done

Lors de l'appel

runScript("path_to_script/test.sh", "1", "2")

sous Unix / Linux, le résultat est:

Received from script: argument 1: 1
Received from script: doubling argument 1: 2
Received from script: argument 2: 2
Received from script: doubling argument 2: 4

Hier est un simple script Windows cmd test.cmd qui compte le nombre d'arguments d'entrée:

@echo off
set a=0
for %%x in (%*) do Set /A a+=1 
echo %a% arguments received

Lors de l'appel du script sous Windows

  runScript("path_to_script\\test.cmd", "1", "2", "3")

La sortie est

Received from script: 3 arguments received
Andrushenko Alexander
la source
1

C'est possible, exécutez-le comme n'importe quel autre programme. Assurez-vous simplement que votre script a le bon #! (she-bang) comme première ligne du script, et assurez-vous qu'il existe des autorisations d'exécution sur le fichier.

Par exemple, s'il s'agit d'un script bash, mettez #! / Bin / bash en haut du script, ainsi que chmod + x.

De plus, si c'est une bonne pratique, non, ce n'est pas le cas, surtout pour Java, mais si cela vous fait gagner beaucoup de temps en portant un gros script, et que vous n'êtes pas payé en plus pour le faire;) économisez votre temps, exécutez le script, et mettez le portage vers Java dans votre liste de tâches à long terme.

Kekoa
la source
1
  String scriptName = PATH+"/myScript.sh";
  String commands[] = new String[]{scriptName,"myArg1", "myArg2"};

  Runtime rt = Runtime.getRuntime();
  Process process = null;
  try{
      process = rt.exec(commands);
      process.waitFor();
  }catch(Exception e){
      e.printStackTrace();
  }  
Girdhar Singh Rathore
la source
1

C'est une réponse tardive. Cependant, j'ai pensé mettre la lutte que j'avais à supporter pour obtenir un script shell à exécuter à partir d'une application Spring-Boot pour les futurs développeurs.

  1. Je travaillais dans Spring-Boot et je n'ai pas pu trouver le fichier à exécuter à partir de mon application Java et il était en train de lancer FileNotFoundFoundException. J'ai dû conserver le fichier dans le resourcesrépertoire et définir le fichier à analyser pom.xmlpendant le démarrage de l'application comme suit.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Après cela, j'avais du mal à exécuter le fichier et il revenait error code = 13, Permission Denied. Ensuite, j'ai dû rendre le fichier exécutable en exécutant cette commande -chmod u+x myShellScript.sh

Enfin, je pourrais exécuter le fichier en utilisant l'extrait de code suivant.

public void runScript() {
    ProcessBuilder pb = new ProcessBuilder("src/main/resources/myFile.sh");
    try {
        Process p;
        p = pb.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

J'espère que cela résout le problème de quelqu'un.

Reaz Murshed
la source
0

Juste la même chose que Solaris 5.10 fonctionne comme ça, ./batchstart.shil y a une astuce que je ne sais pas si votre système d'exploitation l'accepte à la \\. batchstart.shplace. Cette double barre oblique peut aider.

Saul
la source
0

Je pense avec

System.getProperty("os.name"); 

La vérification du système d'exploitation sur peut gérer les scripts shell / bash si ceux-ci sont pris en charge. s'il est nécessaire de rendre le code portable.

DayaMoon
la source
0

pour une utilisation Linux

public static void runShell(String directory, String command, String[] args, Map<String, String> environment)
{
    try
    {
        if(directory.trim().equals(""))
            directory = "/";

        String[] cmd = new String[args.length + 1];
        cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        ProcessBuilder pb = new ProcessBuilder(cmd);

        Map<String, String> env = pb.environment();

        for(String s : environment.keySet())
            env.put(s, environment.get(s));

        pb.directory(new File(directory));

        Process process = pb.start();

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result : " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

public static void runShell(String path, String command, String[] args)
{
    try
    {
        String[] cmd = new String[args.length + 1];

        if(!path.trim().isEmpty())
            cmd[0] = path + "/" + command;
        else
            cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        Process process = Runtime.getRuntime().exec(cmd);

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result: " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

et pour l'utilisation;

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"});

OU

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
Ali Bagheri
la source