1. Información general
Con este tutorial ilustraremos las dos formas de ejecutar un comando de shell desde dentro del código Java .
La primera es usar la clase Runtime y llamar a su método exec .
La segunda y más personalizable forma será crear y utilizar una instancia de ProcessBuilder .
2. Dependencia del sistema operativo
Antes de crear un nuevo proceso ejecutando nuestro comando de shell, primero debemos determinar el sistema operativo en el que se ejecuta nuestra JVM .
Eso es porque, en Windows , necesitamos ejecutar nuestro comando como argumento para el shell cmd.exe y en todos los demás sistemas operativos podemos emitir un shell estándar, llamado sh:
boolean isWindows = System.getProperty("os.name") .toLowerCase().startsWith("windows");
3. Entrada y salida
ademásnecesitamos una forma de conectarnos con los flujos de entrada y salida de nuestro proceso.
Al menos la salida debe consumirse ; de lo contrario, nuestro proceso no regresa correctamente, sino que se bloquea.
Implementemos una clase de uso común llamada StreamGobbler que consume un InputStream :
private static class StreamGobbler implements Runnable { private InputStream inputStream; private Consumer consumer; public StreamGobbler(InputStream inputStream, Consumer consumer) { this.inputStream = inputStream; this.consumer = consumer; } @Override public void run() { new BufferedReader(new InputStreamReader(inputStream)).lines() .forEach(consumer); } }
NOTA: Esta clase está implementando la interfaz Runnable , lo que significa que podría ser ejecutada por cualquier Ejecutor .
4. Runtime.exec ()
Una llamada de método a Runtime.exec () es una forma simple, aún no personalizable, de generar un nuevo subproceso.
En el siguiente ejemplo, solicitaremos un listado de directorios del directorio de inicio de un usuario y lo imprimiremos en la consola:
String homeDirectory = System.getProperty("user.home"); Process process; if (isWindows) { process = Runtime.getRuntime() .exec(String.format("cmd.exe /c dir %s", homeDirectory)); } else { process = Runtime.getRuntime() .exec(String.format("sh -c ls %s", homeDirectory)); } StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println); Executors.newSingleThreadExecutor().submit(streamGobbler); int exitCode = process.waitFor(); assert exitCode == 0;
5. ProcessBuilder
Para la segunda implementación de nuestro problema informático, usaremos un ProcessBuilder . Esto se prefiere al enfoque en tiempo de ejecución porque podemos personalizar algunos detalles.
Por ejemplo, podemos:
- cambiar el directorio de trabajo en el que se ejecuta nuestro comando de shell usando builder.directory ()
- configurar un mapa de clave-valor personalizado como entorno usando builder.environment ()
- Redirigir flujos de entrada y salida a reemplazos personalizados
- heredar ambos a los flujos del proceso JVM actual usando builder.inheritIO ()
ProcessBuilder builder = new ProcessBuilder(); if (isWindows) { builder.command("cmd.exe", "/c", "dir"); } else { builder.command("sh", "-c", "ls"); } builder.directory(new File(System.getProperty("user.home"))); Process process = builder.start(); StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println); Executors.newSingleThreadExecutor().submit(streamGobbler); int exitCode = process.waitFor(); assert exitCode == 0;
6. Conclusión
Como hemos visto en este tutorial rápido, podemos ejecutar un comando de shell en Java de dos formas distintas.
Generalmente, si planea personalizar la ejecución del proceso generado, por ejemplo, para cambiar su directorio de trabajo, debe considerar usar un ProcessBuilder .
Como siempre, encontrarás las fuentes en GitHub .