java - Reading InputStream fra en batchfil proces går til næste linje

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg forsøger at køre en batch-fil med Runtime.exec () og derefter output sin InputStream til et JTextArea. Hvad jeg har arbejdet, men kun delvist. Hvad sker der, er batchfilen kører, men hvis den udfører en anden kommando end noget som 'ekko', afbrydes den kommando straks, og næste linje udføres. Lad os sige, at jeg forsøger at køre en simpel batchfil som denne:


@echo off
echo hello. waiting 5 seconds.
timeout /t 5 /nobreak > NUL
echo finished. goodbye.


Batchfilen udføres, og JTextArea siger


hello. waiting 5 seconds.
finished. goodbye.


men det venter ikke i 5 sekunder i midten.


Jeg kan ikke regne ud, hvorfor det gør det her. Her er hvad jeg bruger til at køre batchfilen og læse dens InputStream.


private class ScriptRunner implements Runnable {
private final GUI.InfoGUI gui; // the name of my GUI class
private final String script;

public ScriptRunner(final GUI.InfoGUI gui, final File script) {
    this.gui = gui;
    this.script = script.getAbsolutePath();
}

@Override
public void run() {
    try {
        final Process p = Runtime.getRuntime().exec(script);
        StreamReader output = new StreamReader(p.getInputStream(), gui);
        Thread t = new Thread(output);
        t.start();
        int exit = p.waitFor();
        output.setComplete(true);
        while (t.isAlive()) {
            sleep(500);
        }
        System.out.println("Processed finished with exit code " + exit);
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

}
private class StreamReader implements Runnable {
private final InputStream is;
private final GUI.InfoGUI gui;

private boolean complete = false;

public StreamReader(InputStream is, GUI.InfoGUI gui) {
    this.is = is;
    this.gui = gui;
}

@Override
public void run() {
    BufferedReader in = new BufferedReader(new InputStreamReader(is));
    try {
        while (!complete || in.ready()) {
            while (in.ready()) {
                gui.setTextAreaText(in.readLine() + "
");
            }
            sleep(250);
        }
    } catch (final Exception e) {
        e.printStackTrace();
    } finally {
            try {
                in.close();
        } catch (final Exception e) {
                e.printStackTrace();
    }
}

public void setComplete(final boolean complete) {
    this.complete = complete;
}

}
public void sleep(final long ms) {
try {
    Thread.sleep(ms);
} catch (final InterruptedException ie) {
}
}


Jeg ved, at min kode er ret rodet, og jeg er sikker på, at den indeholder grammatiske fejl.


Tak for alt hvad du kan gøre for at hjælpe!

Bedste reference


Du 'skaber en Process, men du' læser ikke fra sin standardfejlstrøm. Processen kan skrive meddelelser til sin standardfejl for at fortælle dig, at der er et problem, men hvis du ikke læser standard fejlen, kan du ikke læse disse meddelelser.


Du har to muligheder her:



  1. Når du allerede har en klasse, der stammer fra en strøm (StreamReader), skal du tilslutte en anden af ​​disse til procesens standardfejlstrøm (p.getErrorStream()) og køre den i en anden Thread. Du skal også ringe setComplete om fejlen StreamReader, når opkaldet til p.waitFor() vender tilbage, og vent på, at tråden kører den til at dø.

  2. Erstat din brug af Runtime.getRuntime().exec() med en ProcessBuilder. Denne klasse er ny i Java 5 og giver en alternativ måde at køre eksterne processer på. Efter min mening er den væsentligste forbedring i forhold til Runtime.getRuntime().exec() evnen til at omdirigere procesens standardfejl i standardproduktionen, så du har kun én strøm til at læse fra. [33]



Jeg vil stærkt anbefale at gå til den anden mulighed og vælge at omdirigere procesens standardfejl i standard udgang.


Jeg tog din kode og erstattede linjen


        final Process p = Runtime.getRuntime().exec(script);


med


        final ProcessBuilder pb = new ProcessBuilder(script);
        pb.redirectErrorStream(true);
        final Process p = pb.start();


Jeg har heller ikke din GUI-kode til hånden, så jeg skrev udgangen af ​​processen til System.out i stedet.


Da jeg kørte din kode, fik jeg følgende output:


hello. waiting 5 seconds.
ERROR: Input redirection is not supported, exiting the process immediately.
finished. goodbye.
Processed finished with exit code 0


Havde du set denne fejlmeddelelse, har du måske fået det, at noget var op med kommandoen timeout.


I øvrigt bemærkede jeg i en af ​​dine kommentarer, at ingen af ​​de kommandoer, som ughzan foreslog, fungerede. Jeg erstattede linjen timeout med ping -n 5 127.0.0.1 > NUL og scriptet løb som forventet. Jeg kunne ikke reproducere et problem med dette.

Andre referencer 1


Problemet er helt klart i timeout.exe. Hvis du tilføjer echo \%errorlevel\% efter linjen med timeout, vil du se, at den vender tilbage 1, hvis den kører fra java. Og 0 hvis du kører på sædvanlig måde. Det kræver nok en bestemt konsolfunktionalitet (dvs. markørpositionering), der undertrykkes, når den kører fra java-processen.



  Er der noget, jeg kan gøre for at få dette til at fungere, mens du kører fra Java



Hvis du ikke behøver evnen til at køre nogen -batchfil, så overvej at erstatte timeout med ping. Ellers ... Jeg har forsøgt at køre batchfil med JNA-trough Kernel32.CreateProcess og timeout kører fint. Men så skal du implementere læsning af procesudgang gennem indgående opkald også. [34]


Jeg håber, at nogen vil foreslå en bedre måde.

Andre referencer 2


Metoden ready fortæller kun, om strømmen kan garantere, at noget kan læses straks uden at blokere. Du kan ikke rigtig stole på det, fordi du altid er tilbage false er en gyldig implementering. Strømme med buffere kan kun returnere true, når de har noget buffer. Så jeg formoder, at dit problem er her:


    while (!complete || in.ready()) {
        while (in.ready()) {
            gui.setTextAreaText(in.readLine() + "
");
        }
        sleep(250);
    }


Det bør snarere læse noget som dette:


    String line;
    while (!complete || (line=in.readLine()) != null) {
        gui.setTextAreaText(line + "
");
    }

Andre referencer 3


Det er sandsynligvis fordi din 'timeout ...' kommando returneres med en fejl.


Tre måder at teste det på:



  1. Kontroller, om kommandoen 'timeout ...' fungerer i kommandoprompten til Windows.

  2. Erstat 'timeout ...' i scriptet med 'ping -n 5 127.0.0.1> NUL' (det gør det i det væsentlige)

  3. Fjern alt men 'timeout/t 5/nobreak> NUL' fra dit script. Processen skal returnere med en fejl (1), hvis timeout fejlet, fordi det er den sidste kommando, der udføres.