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?
É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);
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);
}
Créez un runtime pour exécuter chacun des processus. Obtenez le OutputStream du premier runtime et copiez-le dans le InputStream du second.