web-dev-qa-db-fra.com

Comment faire en sorte que les pipes fonctionnent avec Runtime.exec ()?

Considérons le code suivant:

String commandf = "ls /etc | grep release";

try {

    // Execute the command and wait for it to complete
    Process child = Runtime.getRuntime().exec(commandf);
    child.waitFor();

    // Print the first 16 bytes of its output
    InputStream i = child.getInputStream();
    byte[] b = new byte[16];
    i.read(b, 0, b.length); 
    System.out.println(new String(b));

} catch (IOException e) {
    e.printStackTrace();
    System.exit(-1);
}

La sortie du programme est:

/etc:
adduser.co

Bien sûr, lorsque je cours à partir de Shell, cela fonctionne comme prévu:

poundifdef@parker:~/rabbit_test$ ls /etc | grep release
lsb-release

Les internets me disent que, du fait que le comportement du tube n’est pas multiplateforme, les brillants esprits qui travaillent dans l’usine Java qui fabrique Java peuvent ne garantissez pas que les tuyaux fonctionnent.

Comment puis-je faire ceci?

Je ne vais pas faire toute mon analyse en utilisant Java) $ plutôt que grep et sed, _, car si je veux changer de langue, je vais être obligé de réécrire mon code d'analyse syntaxique dans cette langue, qui est totalement impossible.

Comment puis-je faire Java faire la tuyauterie et la redirection lors de l'appel de commandes Shell?

97
poundifdef

Écrivez un script et exécutez-le au lieu de commandes distinctes.

Pipe fait partie de Shell, vous pouvez donc faire quelque chose comme ceci:

String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};

Process p = Runtime.getRuntime().exec(cmd);
171
Kaj

J'ai rencontré un problème similaire sous Linux, sauf qu'il s'agissait de "ps -ef | grep someprocess".
Au moins avec "ls", vous avez un remplacement indépendant de la langue (bien que plus lent) Java. Exemple:

File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
    if (file.matches(.*some.*)) { System.out.println(file); }
}

Avec "ps", c'est un peu plus dur, car Java ne semble pas avoir d'API pour cela.

J'ai entendu dire que Sigar pourrait peut-être nous aider: https://support.hyperic.com/display/SIGAR/Home

Cependant, la solution la plus simple (comme l'a souligné Kaj) consiste à exécuter la commande piped sous la forme d'un tableau de chaînes. Voici le code complet:

try {
    String line;
    String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
    Process p = Runtime.getRuntime().exec(cmd);
    BufferedReader in =
            new BufferedReader(new InputStreamReader(p.getInputStream()));
    while ((line = in.readLine()) != null) {
        System.out.println(line); 
    }
    in.close();
} catch (Exception ex) {
    ex.printStackTrace();
}

Pourquoi le tableau String fonctionne-t-il avec pipe, alors qu'une seule chaîne ne le fait pas ... c'est l'un des mystères de l'univers (surtout si vous n'avez pas lu le code source). Je soupçonne que c’est parce que lorsqu’une chaîne est donnée à exec, il la analyse en premier (d’une manière que nous n’aimons pas). En revanche, lorsque exec reçoit un tableau de chaînes, il le transmet simplement au système d’exploitation sans l’analyser.

En fait, si nous prenons le temps de la journée et regardons le code source (à l’adresse http://grepcode.com/file/repository.grepcode.com/Java/root/jdk/openjdk/6-b14/ Java/lang/Runtime.Java # Runtime.exec% 28Java.lang.String% 2Cjava.lang.String []% 2Cjava.io.File% 29 ), nous constatons que c'est exactement ce qui se passe:

public Process  [More ...] 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);
}
24
Tihamer

Créez un runtime pour exécuter chacun des processus. Obtenez le OutputStream du premier runtime et copiez-le dans le InputStream du second.

6
SJuan76