110 votes

différence entre ProcessBuilder et Runtime.exec()

J'essaie d'exécuter une commande externe à partir d'un code java, mais j'ai remarqué une différence. lors de l'exécution du code :

Process qq=Runtime.getRuntime().exec(
    installation_path + 
    uninstall_path + 
    uninstall_command + 
    uninstall_arguments
);
qq.waitFor();

la valeur de sortie est 0 et la commande s'est terminée correctement.

mais quand j'utilise :

Process qq=(new ProcessBuilder(
    installation_path + 
    uninstall_path + 
    uninstall_command + 
    uninstall_arguments)
).start();
qq.waitFor();

Merci, mais le deuxième code n'était pas correct. J'utilise ProcessBuilder de cette façon et cela ne fonctionne toujours pas :

Process qq=(new ProcessBuilder(
    installation_path +    
    uninstall_path +
    uninstall_command,
    uninstall_arguments)
).start();
qq.waitFor();

la valeur de sortie est 1001 et la commande se termine au milieu bien que waitFor revienne.

Que dois-je faire pour résoudre le problème avec ProcessBuilder ?

112voto

Luke Woodward Points 20417

Les différentes surcharges de Runtime.getRuntime().exec(...) prennent soit un tableau de chaînes, soit une seule chaîne. Les surcharges d'une seule chaîne de caractères de exec() transformera la chaîne de caractères en un tableau d'arguments, avant de transmettre le tableau de chaînes de caractères à l'une des fonctions de l'interface utilisateur. exec() qui prend un tableau de chaînes. Le site ProcessBuilder d'un autre côté, ne prennent qu'un tableau de chaînes de caractères varargs ou un fichier List de chaînes de caractères, où chaque chaîne du tableau ou de la liste est supposée être un argument individuel. D'une manière ou d'une autre, les arguments obtenus sont ensuite réunis en une chaîne de caractères qui est transmise au système d'exploitation pour être exécutée.

Ainsi, par exemple, sur Windows,

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");

exécutera un DoStuff.exe avec les deux arguments donnés. Dans ce cas, la ligne de commande est tokenisée et reconstituée. Cependant,

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");

échouera, sauf s'il existe un programme dont le nom est DoStuff.exe -arg1 -arg2 sur C:\ . Cela est dû au fait qu'il n'y a pas de tokenisation : la commande à exécuter est supposée avoir déjà été tokenisée. Au lieu de cela, vous devez utiliser

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");

ou alternativement

List<String> params = java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);

0 votes

Ça ne marche toujours pas : List<String> params = java.util.Arrays.asList(installation_path+uninstall_path+uninstall_command, uninstall_arguments) ; Process qq=new ProcessBuilder(params).start() ;

7 votes

Je n'arrive pas à croire que cette concaténation de chaînes ait un sens : "installation_path+uninstall_path+uninstall_command".

9 votes

Runtime.getRuntime().exec(...) fait PAS invoquer un shell, sauf si cela est explicitement spécifié par la commande. C'est une bonne chose au regard du récent problème de bogue "Shellshock". Cette réponse est trompeuse, car elle indique que cmd.exe ou équivalent (c'est-à-dire /bin/bash sur unix) serait exécuté, ce qui ne semble pas être le cas. La tokenisation est plutôt effectuée dans l'environnement Java.

21voto

Eugene Lopatkin Points 457

Il n'y a pas de différence entre ProcessBuilder.start() y Runtime.exec() car la mise en œuvre de Runtime.exec() est :

public Process exec(String command) throws IOException {
    return exec(command, null, null);
}

public Process exec(String command, String[] envp, File dir)
    throws IOException {
    if (command.length() == 0)
        throw new IllegalArgumentException("Empty command");

    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();
    return exec(cmdarray, envp, dir);
}

public Process exec(String[] cmdarray, String[] envp, File dir)
    throws IOException {
    return new ProcessBuilder(cmdarray)
        .environment(envp)
        .directory(dir)
        .start();
}

Alors code :

List<String> list = new ArrayList<>();
new StringTokenizer(command)
.asIterator()
.forEachRemaining(str -> list.add((String) str));
new ProcessBuilder(String[])list.toArray())
            .environment(envp)
            .directory(dir)
            .start();

devrait être le même que :

Runtime.exec(command)

Merci dave_thompson_085 pour les commentaires

20voto

Costi Ciudatu Points 13020

Regardez comment Runtime.getRuntime().exec() transmet la commande String au ProcessBuilder . Il utilise un tokenizer et décompose la commande en tokens individuels, puis invoque exec(String[] cmdarray, ......) qui construit un ProcessBuilder .

Si vous construisez le ProcessBuilder avec un tableau de chaînes de caractères au lieu d'une seule, vous obtiendrez le même résultat.

Le site ProcessBuilder prend un String... vararg, donc passer la commande entière comme une seule chaîne a le même effet que d'invoquer cette commande entre guillemets dans un terminal :

shell$ "command with args"

19voto

Stephen C Points 255558

Oui, il y a une différence.

  • Le site Runtime.exec(String) méthode prend une chaîne de commande unique qu'il divise en une commande et une séquence d'arguments.

  • Le site ProcessBuilder Constructeur prend un tableau (varargs) de chaînes de caractères. La première chaîne est le nom de la commande et les autres sont les arguments.

Ainsi, ce que vous demandez à ProcessBuilder, c'est d'exécuter une "commande" dont le nom contient des espaces et d'autres parasites. Bien sûr, le système d'exploitation ne peut pas trouver une commande avec ce nom, et l'exécution de la commande échoue.

0 votes

Non, il n'y a pas de différence. Runtime.exec(String) est un raccourci pour ProcessBuilder. D'autres constructeurs sont pris en charge.

2 votes

Vous avez tort. Lisez le code source ! Runtime.exec(cmd) est effectivement un raccourci pour Runtime.exec(cmd.split("\\s+")) . Le site ProcessBuilder ne dispose pas d'un constructeur qui soit un équivalent direct de la classe Runtime.exec(cmd) . C'est le point que je soulève dans ma réponse.

1 votes

En fait, si vous instanciez un ProcessBuilder comme ceci : new ProcessBuilder("command arg1 arg2") le start() L'appel ne fera pas ce que vous attendez. Il échouera probablement, et ne réussira que si vous avez une commande avec des espaces dans son nom. C'est précisément le problème que pose le PO !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X