簡體   English   中英

process.waitFor() 永遠不會返回

[英]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”。 您可以整天讀取緩沖區,但如果沒有返回任何內容,您的線程將永遠不會釋放它的等待。

希望對某人有所幫助!

有幾種可能性:

  1. 您還沒有消耗進程stdout上的所有輸出。
  2. 您還沒有消耗進程stderr上的所有輸出。
  3. 該進程正在等待您的輸入,而您尚未提供它,或者您尚未關閉該進程的stdin
  4. 這個過程在一個硬循環中旋轉。

正如其他人所提到的,您必須使用stderrstdout

與其他答案相比,自 Java 1.7 以來,它更加容易。 您不必再自己創建線程來讀取stderrstdout

只需使用ProcessBuilder並將方法redirectOutputredirectErrorredirectErrorStream結合使用。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM