Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Odpowiedzi:
Istnieje wiele powodów, które waitFor()
nie wracają.
Ale zwykle sprowadza się to do tego, że wykonywane polecenie nie kończy się.
To znowu może mieć wiele powodów.
Jednym z typowych powodów jest to, że proces generuje pewne dane wyjściowe, a Ty nie czytasz z odpowiednich strumieni. Oznacza to, że proces jest blokowany, gdy tylko bufor się zapełni i czeka, aż proces będzie kontynuował czytanie. Twój proces z kolei czeka na zakończenie drugiego procesu (co nie nastąpi, ponieważ czeka na Twój proces, ...). To jest klasyczna sytuacja impasu.
Musisz stale czytać ze strumienia wejściowego procesów, aby upewnić się, że nie blokuje.
Jest fajny artykuł, który wyjaśnia wszystkie pułapki Runtime.exec()
i pokazuje sposoby ich obejścia, nazwany „Kiedy Runtime.exec () nie będzie” (tak, artykuł pochodzi z 2000 roku, ale treść nadal obowiązuje!)
waitFor()
nie zwraca.
Wygląda na to, że nie czytasz wyjścia, zanim zaczekasz na jego zakończenie. Jest to dobre tylko wtedy, gdy dane wyjściowe nie wypełniają bufora. Jeśli tak, będzie czekać, aż odczytasz wyjście, catch-22.
Być może masz jakieś błędy, których nie czytasz. Spowodowałoby to zatrzymanie aplikacji i czekanie na wieczne oczekiwanie. Prostym sposobem obejścia tego jest przekierowanie błędów do zwykłego wyjścia.
ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("tasklist: " + line);
process.waitFor();
pb.redirectError(new File("/dev/null"));
redirectError
jest dostępna tylko od wersji Java 1.7
Również z dokumentu Java:
java.lang
Proces klasowy
Ponieważ niektóre platformy natywne zapewniają ograniczoną wielkość bufora tylko dla standardowych strumieni wejściowych i wyjściowych, niepowodzenie szybkiego zapisania strumienia wejściowego lub odczytu strumienia wyjściowego podprocesu może spowodować zablokowanie, a nawet zakleszczenie podprocesu.
Niepowodzenie wyczyszczenia bufora strumienia wejściowego (który potokuje do strumienia wyjściowego podprocesu) z Process może prowadzić do zablokowania podprocesu.
Spróbuj tego:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);
gdzie stoutOS i stderrOS to BufferedOutputStream
s, które stworzyłem, aby wypisywać do odpowiednich plików.
process.close()
. Ale kiedy otwieram strumień wejściowy zgodnie z powyższą sugestią i natychmiast zamykam - problem znika. Więc w moim przypadku Wiosna czekała na sygnał zamknięcia strumienia. Mimo że używam automatycznego zamykania Java 8.
Chciałbym coś dodać do poprzednich odpowiedzi, ale ponieważ nie mam przedstawiciela do komentowania, po prostu dodam odpowiedź. Jest to skierowane do użytkowników Androida, którzy programują w języku Java.
Zgodnie z postem od RollingBoy, ten kod prawie działał dla mnie:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
W moim przypadku funkcja waitFor () nie była zwalniana, ponieważ wykonywałem instrukcję bez powrotu („ip adddr flush eth0”). Łatwym sposobem naprawienia tego jest po prostu upewnienie się, że zawsze zwracasz coś w wyciągu. Dla mnie oznaczało to wykonanie następującego polecenia: "ip adddr flush eth0 && echo done". Możesz czytać bufor przez cały dzień, ale jeśli nic nie zostanie zwrócone, Twój wątek nigdy nie zwolni swojego oczekiwania.
Mam nadzieję, że to komuś pomoże!
process.waitFor()
zawiesza, ale to się reader.readLine()
zawiesza, jeśli nie masz wyjścia. Próbowałem użyć waitFor(long,TimeUnit)
limitu czasu, jeśli coś pójdzie nie tak i odkryłem, że był to odczyt zawieszony. Co sprawia, że wersja z przekroczonym limitem czasu wymaga innego wątku do wykonania odczytu ...
Możliwości jest kilka:
stdout
.stderr
.stdin
.Jak wspominali inni, musisz używać stderr i stdout .
W porównaniu z innymi odpowiedziami, ponieważ Java 1.7 jest jeszcze łatwiejsza. Nie musisz już samodzielnie tworzyć wątków, aby czytać stderr i stdout .
Po prostu użyj ProcessBuilder
i użyj tych metod redirectOutput
w połączeniu z albo redirectError
lub redirectErrorStream
.
String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) {
builder.redirectErrorStream(true); // Combine stderr into stdout
} else {
builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();
Z tego samego powodu możesz również użyć inheritIO()
do mapowania konsoli Java z zewnętrzną konsolą aplikacji, na przykład:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
Powinieneś spróbować zużyć wyjście i błąd w tym samym czasie
private void runCMD(String CMD) throws IOException, InterruptedException {
System.out.println("Standard output: " + CMD);
Process process = Runtime.getRuntime().exec(CMD);
// Get input streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
String newLineCharacter = System.getProperty("line.separator");
boolean isOutReady = false;
boolean isErrorReady = false;
boolean isProcessAlive = false;
boolean isErrorOut = true;
boolean isErrorError = true;
System.out.println("Read command ");
while (process.isAlive()) {
//Read the stdOut
do {
isOutReady = stdInput.ready();
//System.out.println("OUT READY " + isOutReady);
isErrorOut = true;
isErrorError = true;
if (isOutReady) {
line = stdInput.readLine();
isErrorOut = false;
System.out.println("=====================================================================================" + line + newLineCharacter);
}
isErrorReady = stdError.ready();
//System.out.println("ERROR READY " + isErrorReady);
if (isErrorReady) {
line = stdError.readLine();
isErrorError = false;
System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);
}
isProcessAlive = process.isAlive();
//System.out.println("Process Alive " + isProcessAlive);
if (!isProcessAlive) {
System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
line = null;
isErrorError = false;
process.waitFor(1000, TimeUnit.MILLISECONDS);
}
} while (line != null);
//Nothing else to read, lets pause for a bit before trying again
System.out.println("PROCESS WAIT FOR");
process.waitFor(100, TimeUnit.MILLISECONDS);
}
System.out.println("Command finished");
}
Myślę, że zauważyłem podobny problem: niektóre procesy rozpoczęły się, wydawały się działać pomyślnie, ale nigdy nie zostały zakończone. Funkcja waitFor () czekała wiecznie, chyba że zabiłem proces w Menedżerze zadań.
Jednak wszystko działało dobrze w przypadkach, gdy długość wiersza poleceń wynosiła 127 znaków lub mniej. Jeśli długie nazwy plików są nieuniknione, możesz chcieć użyć zmiennych środowiskowych, które mogą pozwolić ci zachować krótki ciąg wiersza poleceń. Możesz wygenerować plik wsadowy (za pomocą FileWriter), w którym ustawiasz zmienne środowiskowe przed wywołaniem programu, który chcesz uruchomić. Zawartość takiej partii mogłaby wyglądać następująco:
set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
%MYPROG% %INPUTFILE% %OUTPUTFILE%
Ostatnim krokiem jest uruchomienie tego pliku wsadowego przy użyciu środowiska wykonawczego.
Oto metoda, która działa dla mnie. UWAGA: W tej metodzie jest kod, który może nie mieć zastosowania, więc spróbuj go zignorować. Na przykład „logStandardOut (...), git-bash itp.”.
private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);
ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
builder.command("bash", "-c", doCommand);
}
//Do we need to change dirs?
if (inDir != null) {
builder.directory(new File(inDir));
}
//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
//Start the command line process
process = builder.start();
//This hangs on a large file
// /programming/5483830/process-waitfor-never-returns
//exitCode = process.waitFor();
//This will have both StdIn and StdErr
brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
//Get the process output
String line = null;
String newLineCharacter = System.getProperty("line.separator");
while (process.isAlive()) {
//Read the stdOut
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read the stdErr
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//Nothing else to read, lets pause for a bit before trying again
process.waitFor(100, TimeUnit.MILLISECONDS);
}
//Read anything left, after the process exited
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read anything left, after the process exited
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//cleanup
if (brStdOut != null) {
brStdOut.close();
}
if (brStdErr != null) {
brStdOut.close();
}
//Log non-zero exit values
if (!ignoreErrors && process.exitValue() != 0) {
String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
throw new ExecuteCommandException(exMsg);
}
} catch (ExecuteCommandException e) {
throw e;
} catch (Exception e) {
throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
//Log the results
logStandardOut(stdOut.toString());
logStandardError(stdErr.toString());
}
return stdOut.toString();
}
Asynchroniczne odczytywanie strumienia połączone z unikaniem czekania z przekroczeniem limitu czasu rozwiąże problem.
Możesz znaleźć stronę wyjaśniającą to tutaj http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/