¿Cómo ejecutar el script de shell Unix desde el código Java?

155

Es bastante simple ejecutar un comando Unix desde Java.

Runtime.getRuntime().exec(myCommand);

Pero, ¿es posible ejecutar un script de shell Unix a partir del código Java? En caso afirmativo, ¿sería una buena práctica ejecutar un script de shell desde el código Java?

Lii
fuente
3
Las cosas se ponen interesantes si ese script de shell es interactivo.
ernesto
¿Cuál es la variable myCommand es esa cadena? en caso afirmativo, entonces no funcionará, el método exec requiere String [] y argumento, vea debajo de mi respuesta, funciona perfectamente
Girdhar Singh Rathore

Respuestas:

174

Realmente deberías mirar Process Builder . Realmente está construido para este tipo de cosas.

ProcessBuilder pb = new ProcessBuilder("myshellScript.sh", "myArg1", "myArg2");
 Map<String, String> env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 Process p = pb.start();
Milhous
fuente
3
¿Es una buena práctica llamar guiones desde JAVA? ¿Algún problema de rendimiento?
kautuksahni
1
Tenga en cuenta que es posible que deba especificar el programa / bin / bash o sh para ejecutar el script según la configuración de Java (consulte stackoverflow.com/questions/25647806/… )
Ben Holland
@Milhous Sé que es bastante tarde y las cosas pueden haber cambiado, pero según la documentación actual del Proceso Java, este no es un método recomendado para los scripts de shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Los métodos que crean procesos pueden no funcionar bien para procesos especiales en ciertas plataformas nativas, como procesos de ventanas nativas, procesos de demonio, procesos Win16 / DOS en Microsoft Windows o scripts de shell ".
Harman
24

También puede usar la biblioteca ejecutiva de Apache Commons .

Ejemplo:

package testShellScript;

import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;

public class TestScript {
    int iExitValue;
    String sCommandString;

    public void runScript(String command){
        sCommandString = command;
        CommandLine oCmdLine = CommandLine.parse(sCommandString);
        DefaultExecutor oDefaultExecutor = new DefaultExecutor();
        oDefaultExecutor.setExitValue(0);
        try {
            iExitValue = oDefaultExecutor.execute(oCmdLine);
        } catch (ExecuteException e) {
            System.err.println("Execution failed.");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("permission denied.");
            e.printStackTrace();
        }
    }

    public static void main(String args[]){
        TestScript testScript = new TestScript();
        testScript.runScript("sh /root/Desktop/testScript.sh");
    }
}

Para mayor referencia, también se ofrece un ejemplo en Apache Doc .

No es un error
fuente
¿Puedo ejecutar esto en Windows?
bekur
@KisHanSarsecHaGajjar, ¿también podemos capturar la salida de shellscript y mostrarla en la interfaz de usuario de Java? Quiero saber si es posible hacerlo
Kranthi Sama
@KranthiSama Puede configurar OutputStreampara DefaultExecuterusar DefaultExecuter.setStreamHandlermétodo para la producción de captura en OutputStream. Consulte este hilo para obtener más información: ¿Cómo puedo capturar la salida de un comando ...
No es un error
1
Enlace para agregar la dependencia de la biblioteca de Apache Commons Exec a su proyecto - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead
2
la mejor solucion.
Dev
23

Diría que no está en el espíritu de Java ejecutar un script de shell desde Java. Java está destinado a ser multiplataforma, y ​​ejecutar un script de shell limitaría su uso solo a UNIX.

Dicho esto, definitivamente es posible ejecutar un script de shell desde Java. Usaría exactamente la misma sintaxis que enumeró (no lo he intentado yo mismo, pero intente ejecutar el script de shell directamente, y si eso no funciona, ejecute el shell en sí mismo, pasando el script como un parámetro de línea de comando) .

Jack Leow
fuente
43
Sí, pero de muchas maneras ese mantra "espíritu de Java" o "escribir una vez que se ejecuta en todas partes" es un mito de todos modos.
BobbyShaftoe
15
¿Qué tiene de mítico?
Chris Ballance
15
Lo que es mítico es que generalmente terminas escribiendo numerosas switchesy ifdeclaraciones para evitar todos los matices que no funcionan exactamente igual en diferentes plataformas a pesar de los mejores esfuerzos de las personas que crearon las bibliotecas centrales de Java.
Brian Sweeney
2
¡Sorpresivamente suficiente! Estoy de acuerdo con todos los comentarios anteriores y la respuesta!
AnBisw
11
@BobbyShaftoe He estado escribiendo Java durante 16 años, y siempre desarrollé en Windows, todas mis aplicaciones siempre se implementaron en Solaris / Ibm o cajas Unix con sabor a oráculo, así que no tengo idea de lo que estás hablando
Kalpesh Soni
21

Creo que has respondido tu propia pregunta con

Runtime.getRuntime().exec(myShellScript);

En cuanto a si es una buena práctica ... ¿qué está tratando de hacer con un script de shell que no puede hacer con Java?

Chris Ballance
fuente
Me he enfrentado a una situación similar en la que necesito sincronizar algunos archivos en diferentes servidores cuando ocurre una determinada condición en mi código Java. ¿Hay alguna otra mejor manera?
v kumar
1
@ Chris Ballance ... Sé que este comentario es casi después de 10 años :) pero para responder a su pregunta, ¿qué pasa si mi programa tiene que interactuar con media docena de canales descendentes y ascendentes y depende de su modo de comunicación aceptado? Especialmente cuando estás trabajando en un proyecto que interactúa con tantos canales impares :)
Stunner
Llevar la funcionalidad a un script de shell sería un último esfuerzo si no hay otra forma de hacer el trabajo en Java. Será necesariamente complicado coordinar el estado y las dependencias si está llevando el trabajo a un script de shell. Ocasionalmente, un script de shell es la única forma o el marco de tiempo que lo convierte en la única forma razonable de lograr un poco de trabajo, por lo que esta es una forma de hacerlo.
Chris Ballance
11

Sí, es posible hacerlo. Esto funcionó para mí.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.omg.CORBA.portable.InputStream;

public static void readBashScript() {
        try {
            Process proc = Runtime.getRuntime().exec("/home/destino/workspace/JavaProject/listing.sh /"); //Whatever you want to execute
            BufferedReader read = new BufferedReader(new InputStreamReader(
                    proc.getInputStream()));
            try {
                proc.waitFor();
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            while (read.ready()) {
                System.out.println(read.readLine());
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
Desta Haileselassie Hagos
fuente
7

Aquí está mi ejemplo. Espero que tenga sentido.

public static void excuteCommand(String filePath) throws IOException{
    File file = new File(filePath);
    if(!file.isFile()){
        throw new IllegalArgumentException("The file " + filePath + " does not exist");
    }
    if(isLinux()){
        Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", filePath}, null);
    }else if(isWindows()){
        Runtime.getRuntime().exec("cmd /c start " + filePath);
    }
}
public static boolean isLinux(){
    String os = System.getProperty("os.name");  
    return os.toLowerCase().indexOf("linux") >= 0;
}

public static boolean isWindows(){
    String os = System.getProperty("os.name");
    return os.toLowerCase().indexOf("windows") >= 0;
}
Lionel Yan
fuente
5

¡Sí, es posible y lo has respondido! Acerca de las buenas prácticas, creo que es mejor iniciar comandos desde archivos y no directamente desde su código. Así que hay que hacer de Java ejecutar la lista de comandos (o un comando) en un existentes .bat, .sh, .ksh... archivos. Aquí hay un ejemplo de ejecución de una lista de comandos en un archivo MyFile.sh:

    String[] cmd = { "sh", "MyFile.sh", "\pathOfTheFile"};
    Runtime.getRuntime().exec(cmd);
YANQUI
fuente
4

Para evitar tener que codificar una ruta absoluta, puede usar el siguiente método que buscará y ejecutará su script si está en su directorio raíz.

public static void runScript() throws IOException, InterruptedException {
    ProcessBuilder processBuilder = new ProcessBuilder("./nameOfScript.sh");
    //Sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.
    processBuilder.inheritIO();
    Process process = processBuilder.start();

    int exitValue = process.waitFor();
    if (exitValue != 0) {
        // check for errors
        new BufferedInputStream(process.getErrorStream());
        throw new RuntimeException("execution of script failed!");
    }
}
anataliocs
fuente
3

En cuanto a mí, todas las cosas deben ser simples. Para ejecutar el script solo necesito ejecutar

new ProcessBuilder("pathToYourShellScript").start();
Vladimir Bosyi
fuente
3

La biblioteca ZT Process Executor es una alternativa a Apache Commons Exec. Tiene funcionalidad para ejecutar comandos, capturar su salida, establecer tiempos de espera, etc.

Todavía no lo he usado, pero se ve razonablemente bien documentado.

Un ejemplo de la documentación: ejecutar un comando, bombear el stderr a un registrador y devolver la salida como una cadena UTF8.

 String output = new ProcessExecutor().command("java", "-version")
    .redirectError(Slf4jStream.of(getClass()).asInfo())
    .readOutput(true).execute()
    .outputUTF8();

Su documentación enumera las siguientes ventajas sobre Commons Exec:

  • Manejo mejorado de flujos
    • Leer / escribir en transmisiones
    • Redirigiendo stderr a stdout
  • Manejo mejorado de tiempos de espera
  • Comprobación mejorada de códigos de salida
  • API mejorada
    • One liners para casos de uso bastante complejos
    • One liners para obtener la salida del proceso en una cadena
    • Acceso al objeto Proceso disponible
    • Soporte para procesos asíncronos ( Futuro )
  • Registro mejorado con API SLF4J
  • Soporte para múltiples procesos.
Lii
fuente
2

Aquí hay un ejemplo de cómo ejecutar un script Unix bash o Windows bat / cmd desde Java. Se pueden pasar argumentos en el script y la salida recibida del script. El método acepta un número arbitrario de argumentos.

public static void runScript(String path, String... args) {
    try {
        String[] cmd = new String[args.length + 1];
        cmd[0] = path;
        int count = 0;
        for (String s : args) {
            cmd[++count] = args[count - 1];
        }
        Process process = Runtime.getRuntime().exec(cmd);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        try {
            process.waitFor();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        while (bufferedReader.ready()) {
            System.out.println("Received from script: " + bufferedReader.readLine());
        }
    } catch (Exception ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
    }
}

Cuando se ejecuta en Unix / Linux, la ruta debe ser similar a Unix (con '/' como separador), cuando se ejecuta en Windows: use '\'. Hier es un ejemplo de un script bash (test.sh) que recibe un número arbitrario de argumentos y duplica cada argumento:

#!/bin/bash
counter=0
while [ $# -gt 0 ]
do
  echo argument $((counter +=1)): $1
  echo doubling argument $((counter)): $(($1+$1))
  shift
done

Cuando llame

runScript("path_to_script/test.sh", "1", "2")

en Unix / Linux, el resultado es:

Received from script: argument 1: 1
Received from script: doubling argument 1: 2
Received from script: argument 2: 2
Received from script: doubling argument 2: 4

Hier es un simple script de Windows cmd test.cmd que cuenta el número de argumentos de entrada:

@echo off
set a=0
for %%x in (%*) do Set /A a+=1 
echo %a% arguments received

Al llamar al script en Windows

  runScript("path_to_script\\test.cmd", "1", "2", "3")

La salida es

Received from script: 3 arguments received
Andrushenko Alexander
fuente
1

Es posible, simplemente ejecútelo como cualquier otro programa. ¡Solo asegúrate de que tu script tenga el # correcto! (she-bang) como la primera línea del script y asegúrese de que haya permisos de ejecución en el archivo.

Por ejemplo, si se trata de un script bash, coloque #! / Bin / bash en la parte superior del script, también chmod + x.

También en cuanto a si es una buena práctica, no, no lo es, especialmente para Java, pero si le ahorra mucho tiempo portar un script grande y no le pagan extra por hacerlo;) ahorre tiempo, ejecute el script, y ponga el portado a Java en su lista de tareas a largo plazo.

Kekoa
fuente
1
  String scriptName = PATH+"/myScript.sh";
  String commands[] = new String[]{scriptName,"myArg1", "myArg2"};

  Runtime rt = Runtime.getRuntime();
  Process process = null;
  try{
      process = rt.exec(commands);
      process.waitFor();
  }catch(Exception e){
      e.printStackTrace();
  }  
Girdhar Singh Rathore
fuente
1

Esta es una respuesta tardía. Sin embargo, pensé en poner la lucha que tuve que soportar para ejecutar un script de shell desde una aplicación Spring-Boot para futuros desarrolladores.

  1. Estaba trabajando en Spring-Boot y no pude encontrar el archivo que se ejecutaba desde mi aplicación Java y se estaba lanzando FileNotFoundFoundException. Tuve que mantener el archivo en el resourcesdirectorio y tuve que configurar el archivo para que se escaneara pom.xmlmientras la aplicación se iniciaba de la siguiente manera.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Después de eso tuve problemas para ejecutar el archivo y estaba regresando error code = 13, Permission Denied. Luego tuve que hacer que el archivo fuera ejecutable ejecutando este comando:chmod u+x myShellScript.sh

Finalmente, podría ejecutar el archivo usando el siguiente fragmento de código.

public void runScript() {
    ProcessBuilder pb = new ProcessBuilder("src/main/resources/myFile.sh");
    try {
        Process p;
        p = pb.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Espero que eso resuelva el problema de alguien.

Reaz Murshed
fuente
0

Justo lo mismo que Solaris 5.10 funciona así, ./batchstart.shhay un truco que no sé si su sistema operativo acepta \\. batchstart.sh. Esta doble barra puede ayudar.

Saul
fuente
0

Pienso con

System.getProperty("os.name"); 

La comprobación del sistema operativo puede administrar los scripts de shell / bash si son compatibles. si es necesario que el código sea portátil.

DayaMoon
fuente
0

para uso de linux

public static void runShell(String directory, String command, String[] args, Map<String, String> environment)
{
    try
    {
        if(directory.trim().equals(""))
            directory = "/";

        String[] cmd = new String[args.length + 1];
        cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        ProcessBuilder pb = new ProcessBuilder(cmd);

        Map<String, String> env = pb.environment();

        for(String s : environment.keySet())
            env.put(s, environment.get(s));

        pb.directory(new File(directory));

        Process process = pb.start();

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result : " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

public static void runShell(String path, String command, String[] args)
{
    try
    {
        String[] cmd = new String[args.length + 1];

        if(!path.trim().isEmpty())
            cmd[0] = path + "/" + command;
        else
            cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        Process process = Runtime.getRuntime().exec(cmd);

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result: " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

y para uso;

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"});

O

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
Ali Bagheri
fuente