Cómo Ejecutar un Comando Shell en Java

Descripción General

Con este tutorial ilustraremos las dos formas de ejecutar un comando shell desde dentro del código Java.

La primera es utilizar la clase Runtime y llamar a su método exec.

La segunda forma, más personalizable, será crear y utilizar una instancia de ProcessBuilder.

Dependencia del sistema operativo

Antes de que vayamos a crear un nuevo Process que ejecute nuestro comando de shell, tenemos que determinar primero el sistema operativo en el que se está ejecutando nuestra JVM.

Esto se debe a que, en Windows, tenemos que ejecutar nuestro comando como argumento del cmd.exe y en el resto de sistemas operativos podemos emitir un shell estándar, llamado sh:

boolean isWindows = System.getProperty("os.name") .toLowerCase().startsWith("windows");

Entrada y Salida

Además necesitamos una forma de engancharnos a los flujos de entrada y salida de nuestro proceso.

Al menos la salida debe ser consumida – de lo contrario nuestro proceso no regresa con éxito, en su lugar se colgará.

Implementemos una clase comúnmente utilizada llamada StreamGobbler que consume un InputStream:

private static class StreamGobbler implements Runnable { private InputStream inputStream; private Consumer<String> consumer; public StreamGobbler(InputStream inputStream, Consumer<String> 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 Executor.

Runtime.exec()

Una llamada al método Runtime.exec() es una forma sencilla, aún no personalizable, de generar un nuevo subproceso.

En el siguiente ejemplo solicitaremos un listado de directorios 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;

ProcessBuilder

Para la segunda implementación de nuestro problema informático, utilizaremos un ProcessBuilder. Esto es preferible al enfoque Runtime porque podemos personalizar algunos detalles.

Por ejemplo, podemos:

  • cambiar el directorio de trabajo en el que se ejecuta nuestro comando shell utilizando builder.directory()
  • configurar un mapa clave-valor personalizado como entorno utilizando builder.environment()
  • redirigir los flujos de entrada y salida a reemplazos personalizados
  • heredar ambos a los flujos del proceso actual de la JVM utilizando 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;

Conclusión

Como hemos visto en este rápido tutorial, podemos ejecutar un comando shell en Java de dos formas distintas.

Generalmente, si estás planeando personalizar la ejecución del proceso engendrado, por ejemplo, para cambiar su directorio de trabajo, deberías considerar usar un ProcessBuilder.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.