web-dev-qa-db-fra.com

Java - Exécuter des commandes Bash

J'ai la classe suivante. Cela me permet d'exécuter des commandes via Java.

public class ExecuteShellCommand {

public String executeCommand(String command) {

    StringBuffer output = new StringBuffer();

    Process p;
    try {
        p = Runtime.getRuntime().exec(command);
        p.waitFor();
        BufferedReader reader = 
                        new BufferedReader(new InputStreamReader(p.getInputStream()));

        String line = "";           
        while ((line = reader.readLine())!= null) {
            output.append(line + "\n");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return output.toString();

}

}

Lorsque j'exécute des commandes, le résultat de la commande précédente n'est pas enregistré. Par exemple:

public static void main(String args[]) {

    ExecuteShellCommand com = new ExecuteShellCommand();
    System.out.println(com.executeCommand("ls"));
    System.out.println(com.executeCommand("cd bin"));
    System.out.println(com.executeCommand("ls"));

}

Donne la sortie:

bin
src


bin
src

Pourquoi la deuxième commande "ls" n'affiche-t-elle pas le contenu du répertoire "bin"?

14
mickzer

Vous démarrez un nouveau processus avec Runtime.exec(command). Chaque processus a un répertoire de travail. Il s’agit normalement du répertoire dans lequel le processus parent a été démarré, mais vous pouvez modifier le répertoire dans lequel votre processus est démarré.

Je recommanderais d'utiliser ProcessBuilder

ProcessBuilder pb = new ProcessBuilder("ls");
pb.inheritIO();
pb.directory(new File("bin"));
pb.start();

Si vous souhaitez exécuter plusieurs commandes dans un shell, il est préférable de créer un script shell temporaire et de l'exécuter.

public void executeCommands() throws IOException {

    File tempScript = createTempScript();

    try {
        ProcessBuilder pb = new ProcessBuilder("bash", tempScript.toString());
        pb.inheritIO();
        Process process = pb.start();
        process.waitFor();
    } finally {
        tempScript.delete();
    }
}

public File createTempScript() throws IOException {
    File tempScript = File.createTempFile("script", null);

    Writer streamWriter = new OutputStreamWriter(new FileOutputStream(
            tempScript));
    PrintWriter printWriter = new PrintWriter(streamWriter);

    printWriter.println("#!/bin/bash");
    printWriter.println("cd bin");
    printWriter.println("ls");

    printWriter.close();

    return tempScript;
}

Bien sûr, vous pouvez également utiliser n'importe quel autre script sur votre système. La génération d'un script au moment de l'exécution a parfois du sens, par exemple. si les commandes qui sont exécutées doivent changer. Mais vous devez d’abord essayer de créer un script que vous pouvez appeler avec des arguments au lieu de le générer dynamiquement au moment de l’exécution.

Il peut également être judicieux d’utiliser un moteur de gabarit tel que vélocité si la génération de script est complexe.

15
René Link

Chaque invocation s'exécute dans son propre shell. Ainsi, le "cd" de la 2ème invocation n'est pas vu par la troisième.

Voir: https://docs.Oracle.com/javase/7/docs/api/Java/lang/Runtime.html#exec(Java.lang.String) .

Cela indique que la commande est exécutée dans un processus séparé. Ainsi, vous avez généré 3 processus.

Si vous voulez tous les 3 dans le même processus, essayez ceci:

com.executeCommand("ls; cd bin; ls");
3
EJK

Vous pouvez former une commande bash complexe qui fait tout: "ls; cd bin; ls". Pour que cela fonctionne, vous devez appeler explicitement bash. Cette approche devrait vous donner toute la puissance de la ligne de commande bash (traitement des devis, $ expansion, pipes, etc.). 

/**
 * Execute a bash command. We can handle complex bash commands including
 * multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:
 *     "cd /abc/def; mv ghi 'older ghi '$(whoami)"
 * @param command
 * @return true if bash got started, but your command may have failed.
 */
public static boolean executeBashCommand(String command) {
    boolean success = false;
    System.out.println("Executing BASH command:\n   " + command);
    Runtime r = Runtime.getRuntime();
    // Use bash -c so we can handle things like multi commands separated by ; and
    // things like quotes, $, |, and \. My tests show that command comes as
    // one argument to bash, so we do not need to quote it to make it one thing.
    // Also, exec may object if it does not have an executable file as the first thing,
    // so having bash here makes it happy provided bash is installed and in path.
    String[] commands = {"bash", "-c", command};
    try {
        Process p = r.exec(commands);

        p.waitFor();
        BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line = "";

        while ((line = b.readLine()) != null) {
            System.out.println(line);
        }

        b.close();
        success = true;
    } catch (Exception e) {
        System.err.println("Failed to execute bash with command: " + command);
        e.printStackTrace();
    }
    return success;
}
3
Steve Zobell

chaque commande que vous exécutez a son propre shell bash, donc une fois que vous avez accédé à ce répertoire et pour la commande suivante, vous ouvrez un nouveau shell bash 

essayez de changer votre commande en

ls bin
2
Jigar Joshi

Chaque commande est exécutée individuellement. Ils ne partagent pas le contexte. 

1
vatsal

pour référence future: exécution des commandes bash après cd, dans un sous-répertoire:

import Java.io.BufferedReader;
import Java.io.InputStreamReader;

/*

$ ( D=somewhere/else ; mkdir -p $D ; cd $D ; touch file1 file2 ; )
$ javac BashCdTest.Java && Java BashCdTest
 .. stdout: -rw-r--r-- 1 ubuntu ubuntu 0 Dec 28 12:47 file1
 .. stdout: -rw-r--r-- 1 ubuntu ubuntu 0 Dec 28 12:47 file2
 .. stderr: /bin/ls: cannot access isnt_there: No such file or directory
 .. exit code:2

*/
class BashCdTest
    {
    static void execCommand(String[] commandArr)
        {
        String line;
        try
            {
            Process p = Runtime.getRuntime().exec(commandArr);
            BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((line = stdoutReader.readLine()) != null) {
                // process procs standard output here
                System.out.println(" .. stdout: "+line);
                }
            BufferedReader stderrReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            while ((line = stderrReader.readLine()) != null) {
                // process procs standard error here
                System.err.println(" .. stderr: "+line);
                }
            int retValue = p.waitFor();
            System.out.println(" .. exit code:"+Integer.toString(retValue));
            }
        catch(Exception e)
            { System.err.println(e.toString()); }
        }

    public static void main(String[] args)
        {
        String flist = "file1 file2 isnt_there";
        String outputDir = "./somewhere/else";
        String[] cmd = {
            "/bin/bash", "-c",
            "cd "+outputDir+" && /bin/ls -l "+flist+" && /bin/rm "+flist
            };
        execCommand(cmd);
        }
    }
0
jmullee

Vous pouvez utiliser la commande bash "pmset -g batt" comme dans la méthode suivante qui renvoie le pourcentage de batterie 

public int getPercentage() {
    Process process = null;
    try {
        process = Runtime.getRuntime().exec("pmset -g batt");
    } catch (IOException e) {
        e.printStackTrace();
    }
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            process.getInputStream()));
    String s = null;
    String y = "";
    while (true) {
        try {
            if (!((s = reader.readLine()) != null)) break;
        } catch (IOException e) {
            e.printStackTrace();
        }
        y += s;
        System.out.println("Script output: " + s);
    }
    return Integer.parseInt(y.substring(y.indexOf(')') + 2, y.indexOf('%')));
}
0
Naveen hut