Wie führe ich ein Unix-Shell-Skript aus Java-Code aus?

155

Es ist ganz einfach, einen Unix-Befehl von Java aus auszuführen.

Runtime.getRuntime().exec(myCommand);

Aber ist es möglich, ein Unix-Shell-Skript aus Java-Code auszuführen? Wenn ja, wäre es eine gute Praxis, ein Shell-Skript in Java-Code auszuführen?

Lii
quelle
3
Interessant wird es, wenn das Shell-Skript interaktiv ist.
ernesto
Was ist die Variable myCommand für diesen String? Wenn ja, dann wird es nicht funktionieren, Exec-Methode erfordert String [] und Argument, siehe unten meine Antwort, es funktioniert perfekt
Girdhar Singh Rathore

Antworten:

174

Sie sollten sich Process Builder wirklich ansehen . Es ist wirklich für so etwas gebaut.

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
quelle
3
Ist es empfehlenswert, Skripte von JAVA aufzurufen? Leistungsprobleme?
Kautuksahni
1
Beachten Sie, dass Sie je nach Java-Konfiguration möglicherweise das Programm / bin / bash oder sh angeben müssen, um das Skript auszuführen (siehe stackoverflow.com/questions/25647806/… )
Ben Holland
@Milhous Ich weiß, dass dies ziemlich spät ist und sich möglicherweise etwas geändert hat, aber gemäß der aktuellen Java Process-Dokumentation wird diese Methode für Shell-Skripte nicht empfohlen: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Die Methoden zum Erstellen von Prozessen funktionieren möglicherweise nicht gut für spezielle Prozesse auf bestimmten nativen Plattformen, z. B. native Fensterprozesse, Dämonprozesse, Win16 / DOS-Prozesse unter Microsoft Windows oder Shell-Skripts. "
Harman
24

Sie können auch die Apache Commons Exec-Bibliothek verwenden .

Beispiel:

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

Zur weiteren Bezugnahme wird auch ein Beispiel für Apache Doc gegeben .

Kein Fehler
quelle
Kann ich das unter Windows ausführen?
bekur
@KisHanSarsecHaGajjar können wir auch die Ausgabe von Shellscript erfassen und in der Java-Benutzeroberfläche anzeigen. Ich möchte wissen,
ob
@KranthiSama Sie können festlegen, OutputStreamdass DefaultExecuterdie DefaultExecuter.setStreamHandlerMethode zum Erfassen der Ausgabe in verwendet wird OutputStream. Weitere Informationen finden
Kein Fehler
1
Link zum Hinzufügen der Abhängigkeit der Apache Commons Exec-Bibliothek zu Ihrem Projekt - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead
2
die beste Lösung.
Dev
23

Ich würde sagen, dass es nicht im Sinne von Java ist , ein Shell-Skript von Java aus auszuführen. Java soll plattformübergreifend sein, und das Ausführen eines Shell-Skripts würde seine Verwendung auf nur UNIX beschränken.

Vor diesem Hintergrund ist es definitiv möglich, ein Shell-Skript in Java auszuführen. Sie würden genau dieselbe Syntax verwenden, die Sie aufgelistet haben (ich habe es nicht selbst versucht, aber versuchen Sie, das Shell-Skript direkt auszuführen. Wenn dies nicht funktioniert, führen Sie die Shell selbst aus und übergeben Sie das Skript als Befehlszeilenparameter.) .

Jack Leow
quelle
43
Ja, aber in vielerlei Hinsicht ist das Mantra "Spirit of Java" oder "Write Once Run Everywhere" sowieso ein Mythos.
BobbyShaftoe
15
Was ist daran mythisch?
Chris Ballance
15
Das Mythische daran ist, dass Sie normalerweise zahlreiche switchesund ifAussagen schreiben , um all die Nuancen zu umgehen, die auf verschiedenen Plattformen trotz der besten Bemühungen der Leute, die die Java-Kernbibliotheken entwickelt haben, nicht genau gleich funktionieren.
Brian Sweeney
2
Überraschend genug! Ich stimme allen obigen Kommentaren und der Antwort zu!
AnBisw
11
@BobbyShaftoe Ich schreibe seit 16 Jahren Java und habe es immer unter Windows entwickelt. Alle meine Apps wurden immer auf Unix-Boxen mit Solaris / IBM- oder
Orakelgeschmack bereitgestellt.
21

Ich denke, Sie haben Ihre eigene Frage mit beantwortet

Runtime.getRuntime().exec(myShellScript);

Was ist eine gute Vorgehensweise? Was versuchen Sie mit einem Shell-Skript zu tun, das Sie mit Java nicht tun können?

Chris Ballance
quelle
Ich habe eine ähnliche Situation erlebt, in der ich einige Dateien auf verschiedenen Servern neu synchronisieren muss, wenn eine bestimmte Bedingung in meinem Java-Code auftritt. Gibt es einen anderen besseren Weg?
v Kumar
1
@ Chris Ballance ... Ich weiß, dass dieser Kommentar fast nach 10 Jahren ist :) Aber um Ihre Frage zu beantworten, was ist, wenn mein Programm mit einem halben Dutzend Downstream- und Upstream-Kanälen interagieren muss und von deren akzeptierter Kommunikationsart abhängt. Besonders wenn Sie an einem Projekt arbeiten, das mit so vielen ungeraden Kanälen interagiert :)
Stunner
Die Funktionalität auf ein Shell-Skript zu übertragen, wäre ein letzter Versuch, wenn es keine andere Möglichkeit gibt, die Arbeit in Java zu erledigen. Es wird notwendigerweise kompliziert sein, Status und Abhängigkeiten zu koordinieren, wenn Sie die Arbeit auf ein Shell-Skript übertragen. Gelegentlich ist ein Shell-Skript der einzige Weg, oder der Zeitrahmen macht es zum einzig vernünftigen Weg, um ein bisschen Arbeit zu erledigen. Dies ist also ein Weg, dies zu tun.
Chris Ballance
11

Ja, das ist möglich. Das hat für mich geklappt.

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
quelle
7

Hier ist mein Beispiel. Hoffe es macht Sinn.

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
quelle
5

Ja, das ist möglich und du hast es beantwortet! In Bezug auf bewährte Methoden ist es meiner Meinung nach besser, Befehle aus Dateien und nicht direkt aus Ihrem Code heraus zu starten. So machen Sie müssen Java die Liste von Befehlen (oder einem Befehl) in einer bestehenden auszuführen .bat, .sh, .ksh... Dateien. Hier ist ein Beispiel für die Ausführung einer Liste von Befehlen in einer Datei MyFile.sh:

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

Um zu vermeiden, dass Sie einen absoluten Pfad fest codieren müssen, können Sie die folgende Methode verwenden, mit der Sie Ihr Skript finden und ausführen, wenn es sich in Ihrem Stammverzeichnis befindet.

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
quelle
3

Für mich müssen alle Dinge einfach sein. Zum Ausführen des Skripts muss nur ausgeführt werden

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

Die ZT Process Executor- Bibliothek ist eine Alternative zu Apache Commons Exec. Es verfügt über Funktionen zum Ausführen von Befehlen, Erfassen ihrer Ausgabe, Festlegen von Zeitüberschreitungen usw.

Ich habe es noch nicht benutzt, aber es sieht ziemlich gut dokumentiert aus.

Ein Beispiel aus der Dokumentation: Ausführen eines Befehls, Pumpen des stderr an einen Logger, Zurückgeben der Ausgabe als UTF8-Zeichenfolge.

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

In der Dokumentation sind die folgenden Vorteile gegenüber Commons Exec aufgeführt:

  • Verbesserte Handhabung von Streams
    • Lesen / Schreiben in Streams
    • Stderr nach stdout umleiten
  • Verbesserte Behandlung von Zeitüberschreitungen
  • Verbesserte Überprüfung der Exit-Codes
  • Verbesserte API
    • Ein Liner für recht komplexe Anwendungsfälle
    • Ein Liner, um die Prozessausgabe in einen String zu bekommen
    • Zugriff auf das Prozessobjekt verfügbar
    • Unterstützung für asynchrone Prozesse ( Zukunft )
  • Verbesserte Protokollierung mit der SLF4J-API
  • Unterstützung für mehrere Prozesse
Lii
quelle
2

Hier ist ein Beispiel, wie Sie ein Unix-Bash- oder Windows-Bat / Cmd-Skript von Java aus ausführen. Argumente können an das Skript übergeben und vom Skript empfangen werden. Die Methode akzeptiert eine beliebige Anzahl von Argumenten.

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

Unter Unix / Linux muss der Pfad Unix-ähnlich sein (mit '/' als Trennzeichen), unter Windows '\' verwenden. Hier ist ein Beispiel für ein Bash-Skript (test.sh), das eine beliebige Anzahl von Argumenten empfängt und jedes Argument verdoppelt:

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

Beim Anruf

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

Unter Unix / Linux lautet die Ausgabe:

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 ist ein einfaches cmd Windows-Skript test.cmd, das die Anzahl der Eingabeargumente zählt:

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

Beim Aufrufen des Skripts unter Windows

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

Die Ausgabe ist

Received from script: 3 arguments received
Andrushenko Alexander
quelle
1

Es ist möglich, es einfach wie jedes andere Programm auszuführen. Stellen Sie einfach sicher, dass Ihr Skript die richtige # hat! (she-bang) Zeile als erste Zeile des Skripts, und stellen Sie sicher, dass Ausführungsberechtigungen für die Datei vorhanden sind.

Wenn es sich beispielsweise um ein Bash-Skript handelt, setzen Sie #! / Bin / bash oben in das Skript, auch chmod + x.

Auch wenn es eine gute Praxis ist, nein, es ist nicht, besonders für Java, aber wenn es Ihnen viel Zeit spart, ein großes Skript zu portieren, und Sie dafür nicht extra bezahlt werden;) Sparen Sie Ihre Zeit, führen Sie das aus Skript, und setzen Sie die Portierung nach Java auf Ihre langfristige Aufgabenliste.

Kekoa
quelle
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
quelle
1

Dies ist eine späte Antwort. Ich dachte jedoch daran, den Kampf zu nehmen, den ich ertragen musste, um ein Shell-Skript für zukünftige Entwickler aus einer Spring-Boot-Anwendung auszuführen.

  1. Ich habe in Spring-Boot gearbeitet und konnte die auszuführende Datei in meiner Java-Anwendung nicht finden FileNotFoundFoundException. Ich musste die Datei im resourcesVerzeichnis behalten und die zu scannende Datei so einstellen, dass pom.xmldie Anwendung wie folgt gestartet wurde.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Danach hatte ich Probleme beim Ausführen der Datei und sie kehrte zurück error code = 13, Permission Denied. Dann musste ich die Datei ausführbar machen, indem ich diesen Befehl ausführte -chmod u+x myShellScript.sh

Schließlich konnte ich die Datei mit dem folgenden Code-Snippet ausführen.

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

Hoffe, das löst jemandes Problem.

Reaz Murshed
quelle
0

Genauso wie Solaris 5.10 so funktioniert, ./batchstart.shgibt es einen Trick, den ich nicht kenne, wenn Ihr Betriebssystem die Verwendung \\. batchstart.shstattdessen akzeptiert . Dieser doppelte Schrägstrich kann helfen.

Saul
quelle
0

Ich denke mit

System.getProperty("os.name"); 

Durch Aktivieren des Betriebssystems können die Shell- / Bash-Skripte verwaltet werden, wenn dies unterstützt wird. wenn der Code portabel gemacht werden muss.

DayaMoon
quelle
0

für Linux verwenden

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

und zur Verwendung;

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

ODER

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