Come eseguire lo script shell Unix dal codice Java?


155

È abbastanza semplice eseguire un comando Unix da Java.

Runtime.getRuntime().exec(myCommand);

Ma è possibile eseguire uno script shell Unix dal codice Java? Se sì, sarebbe una buona pratica eseguire uno script di shell all'interno del codice Java?


3
Le cose diventano interessanti se quello script della shell è interattivo.
Ernesto,

qual è la variabile myCommand è quella stringa? se sì, allora non funzionerà, il metodo exec richiede String [] e argomento, vedi sotto il mio Answar, funziona perfettamente
Girdhar Singh Rathore,

Risposte:


174

Dovresti davvero guardare Process Builder . È davvero costruito per questo tipo di cose.

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();

3
È buona norma chiamare script da JAVA? Problemi di prestazioni?
kautuksahni,

1
Si noti che potrebbe essere necessario specificare il programma / bin / bash o sh per eseguire lo script in base alla configurazione di Java (consultare stackoverflow.com/questions/25647806/… )
Ben Holland,

@Milhous So che è abbastanza tardi e che le cose potrebbero essere cambiate ma per la documentazione del processo Java corrente questo non è un metodo raccomandato per gli script di shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " I metodi che creano processi potrebbero non funzionare bene per processi speciali su determinate piattaforme native, come processi di windowing nativi, processi daemon, processi Win16 / DOS su Microsoft Windows o script di shell. "
Harman,

24

Puoi anche usare la libreria exec di Apache Commons .

Esempio :

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");
    }
}

Per ulteriori riferimenti, un esempio è riportato anche su Apache Doc .


Posso eseguirlo su Windows?
Bekur

@KisHanSarsecHaGajjar possiamo anche catturare l'output da shellscript e visualizzarlo nell'interfaccia utente di Java. Voglio sapere se è possibile farlo
Kranthi Sama,

@KranthiSama È possibile impostare OutputStreamper DefaultExecuterutilizzare DefaultExecuter.setStreamHandleril metodo di uscita cattura OutputStream. Si prega di fare riferimento a questa discussione per ulteriori informazioni: Come posso catturare l'output di un comando ...
Non un bug

1
Link per aggiungere la dipendenza della libreria Apache Commons Exec al tuo progetto - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead

2
la migliore soluzione.
Dev

23

Direi che non è nello spirito di Java eseguire uno script di shell da Java. Java è pensato per essere multipiattaforma e l'esecuzione di uno script di shell ne limiterebbe l'utilizzo a UNIX.

Detto questo, è sicuramente possibile eseguire uno script di shell all'interno di Java. Utilizzeresti esattamente la stessa sintassi che hai elencato (non l'ho provato da solo, ma prova a eseguire direttamente lo script della shell e, se non funziona, esegui la shell stessa, passando lo script come parametro della riga di comando) .


43
Sì, ma per molti versi quel mantra "spirito di Java" o "scrivi una volta correre ovunque" è comunque un mito.
BobbyShaftoe,

15
Cosa c'è di mitico a riguardo?
Chris Ballance,

15
ciò che è mitico è che di solito finisci per scrivere numerose switchese ifdichiarazioni per aggirare tutte le sfumature che non funzionano esattamente allo stesso modo su piattaforme diverse nonostante i migliori sforzi delle persone che hanno inventato le librerie core di Java.
Brian Sweeney,

2
Abbastanza sorprendentemente! Sono d'accordo con tutti i commenti sopra e la risposta!
AnBisw,

11
@BobbyShaftoe Scrivo java da 16 anni e ho sempre sviluppato su Windows, tutte le mie app sono sempre state distribuite su scatole unix con aroma solare o ibm, quindi non ho idea di cosa tu stia parlando
Kalpesh Soni,

21

Penso che tu abbia risposto alla tua domanda con

Runtime.getRuntime().exec(myShellScript);

Quanto alla buona pratica ... cosa stai cercando di fare con uno script shell che non puoi fare con Java?


Ho riscontrato una situazione simile in cui ho bisogno di risincronizzare alcuni file su server diversi quando si verifica una determinata condizione nel mio codice Java. C'è un altro modo migliore?
V kumar,

1
@Chris Ballance ... So che questo commento è quasi dopo 10 anni :) ma per rispondere alla tua domanda, cosa succede se il mio programma deve interagire con una mezza dozzina di canali downstream e upstream e dipende dalla loro modalità di comunicazione accettata. Soprattutto quando stai lavorando a un progetto che interagisce con così tanti canali dispari :)
Stunner

Il push della funzionalità su uno script di shell sarebbe uno sforzo disperato se non c'è altro modo di fare il lavoro in Java. Sarà necessariamente complicato coordinare lo stato e le dipendenze se stai spingendo il lavoro verso uno script di shell. Occasionalmente, uno script di shell è l'unico modo o il periodo di tempo lo rende l'unico modo ragionevole per eseguire un po 'di lavoro, quindi questo è un modo per farlo.
Chris Ballance,

11

Sì, è possibile farlo. Questo ha funzionato per me.

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());
        }
    }

7

Ecco il mio esempio Spero abbia senso.

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;
}

5

Sì, è possibile e tu hai risposto! A proposito di buone pratiche, penso che sia meglio lanciare comandi da file e non direttamente dal tuo codice. Quindi devi fare Java eseguire l'elenco dei comandi (o un comando) in un esistente .bat, .sh, .ksh... i file. Ecco un esempio di esecuzione di un elenco di comandi in un file MyFile.sh:

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

4

Per evitare di dover codificare un percorso assoluto, è possibile utilizzare il seguente metodo che troverà ed eseguirà lo script se si trova nella directory principale.

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!");
    }
}

3

Per quanto mi riguarda, tutto deve essere semplice. Per eseguire lo script è sufficiente eseguire

new ProcessBuilder("pathToYourShellScript").start();

3

La libreria ZT Process Executor è un'alternativa ad Apache Commons Exec. Ha funzionalità per eseguire comandi, catturarne l'output, impostare timeout, ecc.

Non l'ho ancora usato, ma sembra ragionevolmente ben documentato.

Un esempio dalla documentazione: eseguire un comando, pompare lo stderr su un logger, restituendo l'output come stringa UTF8.

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

La sua documentazione elenca i seguenti vantaggi rispetto a Commons Exec:

  • Migliore gestione dei flussi
    • Leggere / scrivere su stream
    • Reindirizzamento di stderr a stdout
  • Migliore gestione dei timeout
  • Miglioramento del controllo dei codici di uscita
  • API migliorata
    • Una fodera per casi d'uso piuttosto complessi
    • Una fodera per ottenere l'output del processo in una stringa
    • Accesso all'oggetto Processo disponibile
    • Supporto per processi asincroni ( futuro )
  • Registrazione migliorata con l'API SLF4J
  • Supporto per più processi

2

Ecco un esempio di come eseguire uno script bash Unix o Windows bat / cmd da Java. Gli argomenti possono essere passati allo script e all'output ricevuto dallo script. Il metodo accetta un numero arbitrario di argomenti.

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);
    }
}

Quando si esegue su Unix / Linux, il percorso deve essere simile a Unix (con '/' come separatore), quando si esegue su Windows - usare '\'. Hier è un esempio di uno script bash (test.sh) che riceve un numero arbitrario di argomenti e raddoppia ogni argomento:

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

Quando si chiama

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

su Unix / Linux, l'output è:

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 è un semplice cmd script di Windows test.cmd che conta il numero di argomenti di input:

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

Quando si chiama lo script su Windows

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

L'output è

Received from script: 3 arguments received

1

È possibile, basta eseguirlo come qualsiasi altro programma. Assicurati solo che il tuo script abbia il numero corretto! (she-bang) line come prima riga dello script e assicurati che ci siano permessi di esecuzione sul file.

Ad esempio, se si tratta di uno script bash, inserisci #! / Bin / bash nella parte superiore dello script, anche chmod + x.

Anche se è una buona pratica, no non lo è, specialmente per Java, ma se ti fa risparmiare molto tempo porting su uno script di grandi dimensioni e non ti viene pagato un extra per farlo;) risparmia tempo, esegui il script e inserisci il porting su Java nell'elenco di todo a lungo termine.


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();
  }  

1

Questa è una risposta tardiva. Tuttavia, ho pensato di mettere la lotta che dovevo sopportare per ottenere uno script di shell da eseguire da un'applicazione Spring-Boot per i futuri sviluppatori.

  1. Stavo lavorando in Spring-Boot e non riuscivo a trovare il file da eseguire dalla mia applicazione Java e stava lanciando FileNotFoundFoundException. Ho dovuto mantenere il file nella resourcesdirectory e impostare il file da sottoporre a scansione pom.xmlmentre l'applicazione era avviata come segue.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. In seguito ho avuto problemi nell'esecuzione del file e stava tornando error code = 13, Permission Denied. Quindi ho dovuto rendere eseguibile il file eseguendo questo comando -chmod u+x myShellScript.sh

Infine, ho potuto eseguire il file utilizzando il seguente frammento di codice.

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

Spero che risolva il problema di qualcuno.


0

Proprio come in Solaris 5.10 funziona in questo modo, ./batchstart.shc'è un trucco che non so se il tuo sistema operativo lo accetta \\. batchstart.shinvece. Questa doppia barra può aiutare.


0

Penso con

System.getProperty("os.name"); 

Il controllo del sistema operativo può gestire gli script shell / bash se supportati. se è necessario rendere il codice portatile.


0

per 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);
    }
}

e per 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<>());
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.