Ejecute un comando en Linux usando Java y obtenga el resultado

Estoy usando Groovy para ejecutar comandos en mi caja de Linux y recuperar la salida, pero no puedo usar | tuberías de alguna manera (creo) o tal vez no está esperando a que termine el comando.

¿Qué está mal o qué me falta en mi código?

Mi función de llamada:

def test()
{
    String result="N"

    HashMap<String,String> params = IntermediateResults.get("userparams")
    Map env=AppContext.get(AppCtxProperties.environmentVariables)

    def fClass = new GroovyClassLoader().parseClass( new File( 'plugins/infa9/Infa9CommandExecUtil.groovy' ) )
    List<String> frows=["uname -a",
                        "uname -a | awk '{print\$2}'",
                        "uname -a | cut -d ' ' -f 2"]
    List<String> resultRows = fClass.newInstance().fetchCommandOutput( params, env, frows )

    return result
}

Infa9CommandExecUtil.groovy contenido del archivo (actualización: agregado exitVal println):

package infa9

import java.io.BufferedReader;

public class Infa9CommandExecUtil {
  StringBuffer result

  public Infa9CommandExecUtil() {
    result = new StringBuffer()
  }

  public List<String> fetchCommandOutput( Map<String,String> params, Map env, List<String> rows )
  {

        List<String> outputRows = new ArrayList<String>()
        try
        {
            for(item in rows)
            {
                String temp=item.toString()
                println "CMD:$temp"
                Process proc = Runtime.getRuntime().exec(temp);
                InputStream stdin = proc.getInputStream();
                InputStreamReader isr = new InputStreamReader(stdin);
                BufferedReader br = new BufferedReader(isr);
                String line = null;

                result = new StringBuffer()
                line=null

                int exitVal = proc.waitFor()    //do I need to wait for the thread/process to finish here?

                while ((line = br.readLine()) != null)
                {
                    result.append(line+System.getProperty("line.separator"))    //to maintain the format (newlines)
                }
                String tRes=result
                tRes=tRes.trim()
                println "OUTPUT:$tRes\nEXITVAL:$exitVal"

                outputRows.add(tRes)
            }
        }
        catch (IOException io)  {   io.printStackTrace();}
        catch (InterruptedException ie) {ie.printStackTrace();}
    return  outputRows
  }
}

Mi salida (actualización: valor agregado de exitVal):

CMD:uname -a
OUTPUT:Linux estilo 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 EST 2008 x86_64 x86_64 x86_64 GNU/Linux
EXITVAL:0
CMD:uname -a | awk '{print$2}'
OUTPUT:
EXITVAL:1
CMD:uname -a | cut -d ' ' -f 2
OUTPUT:
EXITVAL:1

Nota: estoy usando internamente sh -c <command>.

preguntado el 08 de noviembre de 11 a las 11:11

Entonces, ¿todavía no lo estás haciendo de una manera Groovy (como te he mostrado algunas veces)? -

@tim: Estoy retrasado, pensé que primero cumpliría con los requisitos y luego refactorizaría el código. Perdón :) -

También leería el flujo de errores o usaría ProcessBuilder para dirigir el error a la salida. Copiaría la salida a un ByteArrayOutputStream y lo convertiría en un String. -

@ Peter Lawrey: ¿Podría dar un ejemplo? Sería de gran ayuda. Gracias -

Pensé que no se suponía que debías llamar a programas que no sean Java desde Java. De lo contrario, ¿por qué molestarse en escribir en Java en primer lugar? -

3 Respuestas

No puede hacer canalizaciones o redireccionamientos usando String.execute(). Esto no funciona en Java, por lo que tampoco funciona en Groovy ...

Puedes usar Process.pipeTo con Groovy para simplificar las cosas:

Process proca = 'uname -a'.execute()
Process procb = 'awk {print\$2}'.execute()

(proca | procb).text

Una versión más genérica podría ser:

String process = 'uname -a | awk {print\$2}'

// Split the string into sections based on |
// And pipe the results together
Process result = process.tokenize( '|' ).inject( null ) { p, c ->
  if( p )
    p | c.execute()
  else
    c.execute()
}
// Print out the output and error streams
result.waitForProcessOutput( System.out, System.out )

respondido 08 nov., 11:16

Gracias, tambien lo que es .inject(null) y p,c-> ¿haciendo aquí? - AabinGunz

@Ab El método de inyección comienza con un valor de acumulador inicial (en este caso null), y luego camina por la Colección aplicando el cierre a cada elemento por turno (el resultado de que el Cierre se coloque en el acumulador para el siguiente elemento). Entonces, en este caso, comenzamos con null, luego la primera iteración lo establece en c.execute() (donde c es nuestro primer elemento en la lista), luego el segundo ciclo, p es el Proceso resultante de esto, por lo que establece el acumulador en p | c.execute(). ¿Espero que esto tenga sentido? - tim_yates

@AbhishekSimon No entiendo la pregunta ... result.waitForProcessOutput( System.out, System.out ) redirige los flujos de salida y error de la cadena de proceso a System.out. Entonces todo se muestra en la misma transmisión ... - tim_yates

Gracias funcionó a las mil maravillas. Publicaré la función completa en mi pregunta como una actualización para otros también :) - AabinGunz

El tubo | es una característica de un shell como bash. Para usar una tubería, necesita ejecutar un shell, como

"/bin/bash", "-c", "uname -a | awk '{print $2}'"

Para usar ProcessBuilder con redirección, puede hacer

public static void main(String... args) throws IOException, InterruptedException {
    final String cmd = "uname -a | awk '{print $1 \" \" $3}'";
    System.out.println(cmd + " => " + run(cmd));
}

private static String run(String cmd) throws IOException, InterruptedException {
    final ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", cmd);
    pb.redirectErrorStream();
    final Process process = pb.start();
    final InputStream in = process.getInputStream();
    final byte[] bytes = new byte[1024];
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // you must read the output as the program runs or it can stall.
    for (int len; (len = in.read(bytes)) > 0;)
        baos.write(bytes, 0, len);
    process.waitFor();
    return baos.toString(); // assuming the default char encoding is ok.
}

huellas dactilares

uname -a | awk '{print $1 " " $3}' => Linux 2.6.18-274.3.1.el5

respondido 08 nov., 11:16

La tubería es una característica de la cáscara. Entonces, si desea que las tuberías sean compatibles, debe ejecutar su comando en el contexto de shell, es decir, la línea de comando a la que pasa exec en java debería verse así /bin/sh YOUR_COMMAND.

respondido 08 nov., 11:15

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.