Come far funzionare i tubi con Runtime.exec ()?

Considera il seguente codice:

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); } 

L’output del programma è:

 /etc: adduser.co 

Quando corro dalla shell, ovviamente, funziona come previsto:

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

Gli interni mi dicono che, a causa del fatto che il comportamento dei tubi non è multipiattaforma, le brillanti menti che lavorano nello stabilimento Java che produce Java non possono garantire che i tubi funzionino.

Come posso fare questo?

Non ho intenzione di eseguire tutte le mie analisi usando i costrutti Java piuttosto che grep e sed , perché se voglio cambiare il linguaggio, sarò costretto a riscrivere il mio codice di analisi in quella lingua, che è totalmente un no- partire.

Come posso rendere Java fare piping e reindirizzamento quando si chiamano i comandi di shell?

Scrivi uno script ed esegui lo script invece di comandi separati.

Pipe è una parte della shell, quindi puoi anche fare qualcosa del genere:

 String[] cmd = { "/bin/sh", "-c", "ls /etc | grep release" }; Process p = Runtime.getRuntime().exec(cmd); 

Mi sono imbattuto in un problema simile in Linux, tranne che era “ps -ef | grep someprocess”.
Almeno con “ls” hai una sostituzione Java indipendente dalla lingua (anche se più lenta). Per esempio.:

 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); } } 

Con “ps”, è un po ‘più difficile, perché Java non sembra avere un’API per questo.

Ho sentito che Sigar potrebbe aiutarci: https://support.hyperic.com/display/SIGAR/Home

La soluzione più semplice, tuttavia, (come sottolineato da Kaj) è eseguire il comando piped come array di stringhe. Ecco il codice completo:

 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(); } 

Per quanto riguarda il motivo per cui l’array String funziona con pipe, mentre una singola stringa non … è uno dei misteri dell’universo (specialmente se non hai letto il codice sorgente). Ho il sospetto che sia perché quando exec viene data una singola stringa, la analizza prima (in un modo che non ci piace). Al contrario, quando exec viene fornito un array di stringhe, lo passa semplicemente al sistema operativo senza analizzarlo.

In realtà, se prendiamo del tempo libero e guardiamo il codice sorgente ( 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 ), scopriamo che è esattamente ciò che sta accadendo:

 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); } 

Creare un runtime per eseguire ognuno dei processi. Prendi l’OutputStream dal primo Runtime e copialo in InputStream dal secondo.