CMD fenster Schließen nach abarbeitung des Befehls

Hi,

ich starte in meiner Java Anwendung ein cmd Fernster und darin SQLPlus inkl. der Ausführung eines SQL Skripts.

Ich will nach dem das Skript fertig durchgelaufen ist (es wird am ende eine log file nach C Temp geschrieben) das logfile in einer TextArea ausgeben. Das hab ich auch eigentlich schon alles, nur wird das Logfile eben angezeigt bevor das Skript durchgelaufen ist. Ein sleep ist nicht das was ich will, da das Skript nicht immer gleich lange läuft.

Kann man irgendwie abfragen wann das Skript fertig druchgelaufen ist und dann erst das log anzeigen lassen?

ja - Du musst auf das Ende des Prozesses warten process.waitFor();

ProcessBuilder builder = new ProcessBuilder(cmd);
Process process = builder.start();
process.waitFor();

Und dann muss der letzte Befehl in der CMD-Zeile sein:
exit

Damit wird das CMD-Fenster geschlossen.

Du kannst (in Windows) den Befehl “start” davor schreiben, dann wird alles in einer neuen Konsole ausgeführt die sich danach wieder schließt:

In Konsole:
start sqlplus -V

Via Java:
cmd /C start sqlplus -V

Ob das Fenster geschlossen wird oder nicht, hängt davon ab, mit welcher Option cmd gestartet wird:
/c = Fenster wird geschlossen
/k = Fenster bleibt offen

C:\Windows\System32>cmd /?
Startet eine neue Instanz des Windows Befehlsinterpreters.

[noparse]CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] Zeichenfolge][/noparse]

/C Führt den Befehl in der Zeichenfolge aus und endet dann.
/K Führt den Befehl in der Zeichenfolge aus und endet dann nicht.

Hi,

ich habe jetzt folgendes:

String command = "cmd /c start cmd.exe /c \"sqlplus .....@sqlFile.sql")

Process child = Runtime.getRuntime().exec(command);
child.waitFor();
logFileOutput();

Aber das Logfile wird trotzdem sofort ausgegeben ohne zu warten!

Du darfst kein start verwenden, damit wird ja aus der Konsole heraus ein neuer Prozess gestartet, auf den Du in Java Programm nicht mehr warten kannst. Der ursprüngliche aus Java heraus gestartet Prozess beendet sich sofort wieder nach dem der Subprozess gestartet wurde.

Ok, aber wie ist dann der korrekte Aufruf damit das funktioniert?

wenn ich String command = "cmd /c cmd.exe /c \"sqlplus .....@sqlFile.sql") es so verwende, ohne start dann passiert nichts, bzw. die Anwendung läuft in der Schleife.
Wenn ich sie dann abwürge wird das Commando anscheinend noch ausgeführt.

Und das zweite cmd.exe /c ist auch unnötig, es reicht einfach

"cmd /c sqlplus ..."

So sorry, bin lästig, aber…

ich habe jetzt folgendes:

String command = "cmd /c sqlplus aaa/bbb@aaa.aaa.aa @" + sqlFile;     
      
        try {
            Process child = Runtime.getRuntime().exec(command);
            child.waitFor();
            logFileOutput();
        } catch (IOException ex) {
            Logger.getLogger(UpdateSQLSkriptSQLPlus.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ex) {
            Logger.getLogger(UpdateSQLSkriptSQLPlus.class.getName()).log(Level.SEVERE, null, ex);
        }

Der Prozess wird aber anscheinend nicht beendet. Sanduhr und … es passiert nichts :slight_smile:

P.S.: ohne dem waitFor läuft das Skript durch.
Nachtrag… leider hängt es sich auch ohne dem waitfor auf!

Wenn ich es gerade richtig überblicke startest Du mit dem Command ja nicht einfach nur einen Prozess der durchläuft und von alleine beendet, sondern Du startest die sqlplus Anwendung und die muss auch wieder beendet werden.
Spontan fallen wir da zwei Möglichkeiten ein:

  1. Du schreibst einen Batch der sqlplus startet das Skript ausführt und die Anwendung wieder beendet. Aus Deiner Java Applikation rufst Du diesen Batch auf.
  2. Du holst Dir die Input und Output Streams des Processes und kommunizierst mit der Anwendung und kannst sie somit auch wieder beenden.

Evlt. biete sqlplus selbst noch weitere Möglichkeiten?

Der externe Prozess (also cmd und sqlplus) blockiert, weil seine Standardausgabe nicht ausgelesen wird.
Man muss sich den InputStream vom Process-Object (hier: child) holen und in einem separaten Thread auslesen.

  1. weg mit CMD !
    der befehlszeileninterpreter ist kein würg-a-raund spielzeug um beliebige prozesse zu starten sondern um gewisse befehle in text-form zu verarbeiten

lösung : prozess direkt starten !

  1. wie wäre es sich mal mit der API zu befassen ? dann wüsste man auch das es statt Runtime.exec() eine bessere möglichkeit gibt : hier einfügen
    (ich werds jetzt mal nicht spoilern, das darfst du gerne alleine rausbekommen)

  2. jeder prozess hat 3 streams : einen inputstream, einen outputstream und einen errorstream
    so wie es aussieht hast du dich noch nicht damit befasst, ansonsten wüsstest du worauf zu achten ist wenn man mit streams arbeitet

um es kurz zu machen : die frage wurde in der form schon zig tausend mal gestellt und beantwortet, und lässt sich auch in der “korrekten” form über google finden

Ich denke, dass (unabhängig von den Einwänden, inwieweit die grundsätzliche Vorgehensweise hier sinnvoll ist) Peter_W_Marth den entscheidenden Hinweis schon gegeben hat: Die Streams müssen “leergelesen” werden, bevor es weitergehen kann (hatte das mal irgendwo aufgeschnappt, und es scheint zu stimmen ;)). Das muss aber wohl nicht in einem eigenen Thread passieren. Ich mache da manchmal sowas…:

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

    String errorMessage =
        new String(toByteArray(process.getErrorStream()));
    String outputMessage =
        new String(toByteArray(process.getInputStream()));
    int exitValue = 0;
    try
    {
        exitValue = process.waitFor();
    }
    catch (InterruptedException e)
    {
        Thread.currentThread().interrupt();
    }
...

/**
 * Fully reads the given InputStream and returns it as a byte array
 *
 * @param inputStream The input stream to read
 * @return The byte array containing the data from the input stream
 * @throws IOException If an I/O error occurs
 */
private static byte[] toByteArray(InputStream inputStream)
    throws IOException
{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte buffer[] = new byte[8192];
    while (true)
    {
        int read = inputStream.read(buffer);
        if (read == -1)
        {
            break;
        }
        baos.write(buffer, 0, read);
    }
    return baos.toByteArray();
}

Vielleicht hilft’s …

Danke!

@Marco13 : Völlig korrekt. Den separaten Thread nutze ich eigentlich nur, um die Konsolenausgaben zeitgleich mit der Verarbeitung in einer JTextArea anzuzeigen bzw. zu protokollieren.