¿Cómo hacer que las tuberías funcionen con Runtime.exec ()?

Considera el siguiente código:

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 salida del progtwig es:

 /etc: adduser.co 

Cuando corro desde el caparazón, por supuesto, funciona como se esperaba:

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

Las redes internas me dicen que, debido a que el comportamiento de las tuberías no es multiplataforma, las mentes shinys que trabajan en la fábrica de Java que produce Java no pueden garantizar que las tuberías funcionen.

¿Cómo puedo hacer esto?

No voy a hacer todo mi análisis utilizando construcciones de Java en lugar de grep y sed , porque si quiero cambiar el idioma, me veré obligado a volver a escribir mi código de análisis en ese idioma, que es totalmente un no- ir.

¿Cómo puedo hacer que Java haga las canalizaciones y la redirección al llamar comandos de shell?

Escriba una secuencia de comandos y ejecute la secuencia de comandos en lugar de comandos separados.

Pipe es parte del shell, por lo que también puedes hacer algo como esto:

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

Me encontré con un problema similar en Linux, excepto que fue “ps -ef | grep someprocess”.
Al menos con “ls” tiene un reemplazo de Java independiente del lenguaje (aunque más lento). P.ej.:

 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”, es un poco más difícil, porque Java no parece tener una API para ello.

He oído que Sigar podría ayudarnos: https://support.hyperic.com/display/SIGAR/Home

La solución más simple, sin embargo, (como lo señala Kaj) es ejecutar el comando canalizado como una matriz de cadenas. Aquí está el código 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(); } 

En cuanto a por qué la matriz String funciona con pipe, mientras que una sola cadena no … es uno de los misterios del universo (especialmente si no ha leído el código fuente). Sospecho que es porque cuando se le da una sola cadena a un ejecutivo, primero la analiza (de una manera que no nos gusta). Por el contrario, cuando se le entrega a exec una matriz de cadenas, simplemente la pasa al sistema operativo sin analizarla.

En realidad, si nos tomamos el tiempo fuera del día ocupado y miramos el código fuente (en 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 ), encontramos que eso es exactamente lo que está sucediendo:

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

Crea un Runtime para ejecutar cada uno de los procesos. Obtenga el OutputStream desde el primer Runtime y cópielo en el InputStream desde el segundo.