[英]process.waitFor() never returns
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
waitFor()
沒有返回的原因有很多。
但這通常歸結為執行的命令沒有退出的事實。
同樣,這可能有很多原因。
一個常見的原因是該過程產生了一些輸出,而您沒有從適當的流中讀取。 這意味着一旦緩沖區已滿,該進程就會被阻止並等待您的進程繼續讀取。 您的進程反過來等待另一個進程完成(它不會因為它等待您的進程,...)。 這是典型的僵局情況。
您需要不斷地從進程輸入流中讀取,以確保它不會阻塞。
有一篇很好的文章解釋了Runtime.exec()
的所有缺陷,並展示了繞過它們的方法,稱為“當 Runtime.exec() 不會” (是的,這篇文章來自 2000 年,但內容仍然適用!)
在等待它完成之前,您似乎沒有閱讀輸出。 僅當輸出未填充緩沖區時才可以。 如果是這樣,它將等到您讀取輸出,catch-22。
也許你有一些你沒有閱讀的錯誤。 這將導致應用程序停止並 waitFor 永遠等待。 解決此問題的一種簡單方法是將錯誤重定向到常規輸出。
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();
同樣來自 Java 文檔:
java.lang
上課過程
由於一些原生平台只為標准輸入輸出流提供有限的緩沖區大小,未能及時寫入子進程的輸入流或讀取輸出流可能會導致子進程阻塞,甚至死鎖。
無法從 Process 清除輸入流的緩沖區(通過管道連接到子進程的輸出流)可能會導致子進程阻塞。
嘗試這個:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
我想在之前的答案中添加一些內容,但由於我沒有代表發表評論,所以我只會添加一個答案。 這是針對使用 Java 編程的 android 用戶。
根據 RollingBoy 的帖子,這段代碼幾乎對我有用:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
就我而言,waitFor() 沒有釋放,因為我正在執行一個沒有返回的語句(“ip adddr flush eth0”)。 解決此問題的一種簡單方法是確保您始終在語句中返回某些內容。 對我來說,這意味着執行以下命令:“ip adddr flush eth0 && echo done”。 您可以整天讀取緩沖區,但如果沒有返回任何內容,您的線程將永遠不會釋放它的等待。
希望對某人有所幫助!
有幾種可能性:
stdout
上的所有輸出。stderr
上的所有輸出。stdin
。正如其他人所提到的,您必須使用stderr和stdout 。
與其他答案相比,自 Java 1.7 以來,它更加容易。 您不必再自己創建線程來讀取stderr和stdout 。
只需使用ProcessBuilder
並將方法redirectOutput
與redirectError
或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();
出於同樣的原因,您還可以使用inheritIO()
將 Java 控制台與外部應用程序控制台映射,例如:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
您應該嘗試同時消耗輸出和錯誤
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");
}
這是一種對我有用的方法。 注意:此方法中的某些代碼可能不適用於您,因此請嘗試忽略它。 例如“logStandardOut(...)、git-bash 等”。
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
// https://stackoverflow.com/questions/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();
}
我想我觀察到了一個類似的問題:一些進程已啟動,似乎運行成功但從未完成。 函數 waitFor() 一直在等待,除非我在任務管理器中終止了該進程。
但是,在命令行長度為 127 個字符或更短的情況下,一切都運行良好。 如果長文件名是不可避免的,您可能需要使用環境變量,這可以讓您保持命令行字符串簡短。 您可以生成一個批處理文件(使用 FileWriter),您可以在其中設置環境變量,然后再調用您實際要運行的程序。 這樣一個批次的內容可能如下所示:
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%
最后一步是使用 Runtime 運行這個批處理文件。
異步讀取流結合避免等待超時將解決問題。
您可以在此處找到解釋此內容的頁面http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/
public static void main(String[] args) throws PyException, IOException, InterruptedException
這些應該是拋出的異常
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.