CodeGym /Cursos /JAVA 25 SELF /ProcessBuilder — ejecución de procesos externos

ProcessBuilder — ejecución de procesos externos

JAVA 25 SELF
Nivel 61 , Lección 0
Disponible

1. Qué son los procesos en el SO y por qué ejecutarlos desde Java

Procesos: de la JVM a bash y vuelta

Cuando enciendes el ordenador y abres el navegador, el mensajero o un juego — todo eso son procesos independientes. El sistema operativo arranca para cada programa su propio «miniuniverso»: asigna memoria, tiempo de CPU y permisos para trabajar con archivos y red. Así, los programas conviven sin estorbarse.

Una aplicación Java no es una excepción. Cuando ejecutas java MyApp, el sistema crea para ella un proceso independiente con todos los recursos necesarios. Dentro de ese proceso se ejecuta tu programa: calcula, dibuja, lee archivos — todo lo que le corresponde.

Pero a veces Java no basta. Puede que necesites pedir ayuda a otro programa: lanzar un compresor para empaquetar archivos, invocar ffmpeg para procesar vídeo, o simplemente averiguar qué versión de Java hay instalada en el equipo. Eso es ejecutar un proceso externo: Java le dice al sistema «llama a esta utilidad», y luego recibe el resultado de su trabajo.

En esencia, es una forma de hacer tu programa más flexible: combinar distintas herramientas, automatizar tareas rutinarias o integrar tu lógica con procesos del sistema ya existentes. A veces es más fácil pedir a un comando externo que haga parte del trabajo que reinventar la rueda en el código.

JVM vs. proceso externo

Proceso de la JVM — tu aplicación ejecutándose en la Máquina Virtual de Java.

Proceso externo — cualquier otro programa: una calculadora, un script de Python, la línea de comandos, incluso otra instancia de Java.

2. La clase ProcessBuilder

En los «antiguos» tiempos, Java lanzaba procesos mediante el método Runtime.getRuntime().exec(). No era la forma más cómoda ni segura — como intentar clavar un clavo con un microscopio. Por eso, desde Java 5 apareció la clase ProcessBuilder, que permite crear, configurar y ejecutar procesos externos de forma más flexible y clara.

ProcessBuilder es una especie de «constructor» que permite definir de antemano todos los parámetros del proceso futuro: comando, argumentos, carpeta de trabajo, variables de entorno, etc.

Sintaxis: creación de un proceso

ProcessBuilder pb = new ProcessBuilder("comando", "argumento1", "argumento2", ...);
  • El primer argumento es el nombre del comando (por ejemplo, "ls", "dir", "ping", "java").
  • Los demás son los parámetros del comando.

Ejemplo: ejecutar el comando ls (Linux/Mac) o dir (Windows)

ProcessBuilder pb;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
    pb = new ProcessBuilder("cmd.exe", "/c", "dir");
} else {
    pb = new ProcessBuilder("ls", "-l");
}

Por cierto, en Windows comandos como dir, copy, etc. no son ejecutables independientes, sino órdenes «integradas» en la consola (cmd.exe). Por eso deben ejecutarse mediante cmd.exe /c ....

Ejemplo: ejecutar un proceso simple

ProcessBuilder pb = new ProcessBuilder("echo", "Hello, Java!");

3. Configuración del entorno del proceso

Paso de argumentos. Los argumentos del comando se pasan como cadenas separadas:

ProcessBuilder pb = new ProcessBuilder("ping", "google.com");

Indicar el directorio de trabajo. Por defecto, el proceso se inicia en la misma carpeta que tu programa. Pero puedes indicar explícitamente otra:

pb.directory(new java.io.File("/tmp"));      // Para Linux/Mac
pb.directory(new java.io.File("C:\\Temp"));  // Para Windows

Modificar variables de entorno. Cada proceso tiene su propio conjunto de variables de entorno. Se pueden añadir o modificar:

pb.environment().put("MY_VAR", "HelloFromJava");

Puede ser útil si el proceso externo espera variables específicas.

4. Ejecución del proceso

Método start(). Cuando ya lo has configurado todo, es hora de lanzar el proceso:

Process process = pb.start();

El método start() devuelve un objeto Process, que permite gestionar el programa lanzado: leer su salida, escribir en su entrada, terminarlo, etc.

Manejo de excepciones. start() puede lanzar IOException si no se encuentra el comando, faltan permisos o se produce otro error de inicio.

Ejemplo:

try {
    Process process = pb.start();
    // Trabajamos con el proceso...
} catch (IOException e) {
    System.out.println("Error al iniciar el proceso: " + e.getMessage());
}

5. Práctica: ejecutar comandos simples

Ejemplo 1: mostrar la lista de archivos de una carpeta

import java.io.*;

public class ProcessDemo {
    public static void main(String[] args) {
        // Determinamos el comando según el sistema operativo
        ProcessBuilder pb;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            pb = new ProcessBuilder("cmd.exe", "/c", "dir");
        } else {
            pb = new ProcessBuilder("ls", "-l");
        }

        try {
            Process process = pb.start();

            // Leemos la salida del proceso (stdout)
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
            );
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // Esperamos a que termine el proceso
            int exitCode = process.waitFor();
            System.out.println("El proceso ha finalizado con el código: " + exitCode);

        } catch (IOException | InterruptedException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

¿Qué ocurre aquí?

  • Determinamos qué SO hay para elegir el comando adecuado.
  • Creamos ProcessBuilder con el comando necesario.
  • Lanzamos el proceso con start().
  • Leemos las líneas de stdout del proceso y las mostramos por pantalla.
  • Esperamos a que el proceso termine (waitFor()).
  • Mostramos el código de retorno (0 — éxito; otro valor — error).

Ejemplo 2: ejecutar java -version

ProcessBuilder pb = new ProcessBuilder("java", "-version");
try {
    Process process = pb.start();

    // java -version escribe en stderr, por eso leemos getErrorStream()
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getErrorStream())
    );
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    process.waitFor();
} catch (IOException | InterruptedException e) {
    e.printStackTrace();
}

Matiz importante: Algunas órdenes (por ejemplo, java -version) no escriben en la salida estándar (stdout), sino en el flujo de errores (stderr). Por eso a veces hay que leer process.getErrorStream().

6. Multiplataforma: diferencias entre Windows y Linux/Mac

  • Los comandos y sus parámetros pueden diferir.
  • Las rutas de archivos se escriben de forma distinta (C:\Temp frente a /tmp).
  • Algunos comandos (por ejemplo, ls, cat) solo existen en sistemas tipo Unix, y en Windows se sustituyen por análogos (dir, type).
  • En Windows las órdenes integradas se ejecutan únicamente mediante cmd.exe /c comando.

Ejemplo de comprobación del SO:

String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
    // Windows
} else if (os.contains("mac")) {
    // macOS
} else if (os.contains("nix") || os.contains("nux")) {
    // Linux
}

Consejo: Siempre prueba tus programas en los SO de destino si buscas multiplataforma.

7. Tabla: métodos y opciones principales de ProcessBuilder

Método/campo Propósito Ejemplo de uso
new ProcessBuilder(String...)
Crear un proceso con comando y argumentos
new ProcessBuilder("ls", "-l")
.directory(File)
Establecer el directorio de trabajo
.directory(new File("/tmp"))
.environment()
Obtener/modificar las variables de entorno
.environment().put("VAR", "value")
.start()
Iniciar el proceso
Process p = pb.start()
Process.getInputStream()
Obtener stdout del proceso
InputStream
Process.getErrorStream()
Obtener stderr del proceso
InputStream
Process.getOutputStream()
Obtener stdin del proceso
OutputStream
Process.waitFor()
Esperar la finalización del proceso
int code = p.waitFor()
Process.exitValue()
Obtener el código de retorno del proceso
int code = p.exitValue()

8. Errores típicos al ejecutar procesos externos

Error n.º 1: comando no encontrado. Si te equivocas en el nombre del comando o no existe en el sistema, obtendrás IOException: Cannot run program .... Por ejemplo, intentar ejecutar ls en Windows.

Error n.º 2: paso de argumentos incorrecto. ¡No debes unir todo el comando en una sola cadena! Correcto: new ProcessBuilder("ping", "google.com"). Incorrecto: new ProcessBuilder("ping google.com").

Error n.º 3: no tener en cuenta las diferencias del SO. Un comando que funciona perfectamente en Linux puede no existir en Windows, y viceversa. Comprueba siempre el SO y adapta el comando.

Error n.º 4: no procesar la salida del proceso. Si no lees la salida del proceso, puede «quedarse colgado» por desbordamiento del búfer. Aunque no vayas a usarla, léela y, por ejemplo, descártala.

Error n.º 5: no cerrar los flujos. Los flujos del proceso deben cerrarse tras su uso para evitar fugas de recursos.

Error n.º 6: no manejar las excepciones. Lanzar un proceso externo conlleva riesgos. Usa siempre try-catch e informa al usuario de los errores.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION