process.waitFor ()는 절대 반환하지 않습니다.


95
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();

JAVA 8에는 시간 제한을 지정할 수있는 waitFor 오버로드가 있습니다. waitFor가 반환되지 않는 경우를 피하는 것이 더 나은 선택 일 수 있습니다.
Ikaso 2015-06-01

답변:


145

waitFor()돌아 오지 않는 데는 여러 가지 이유가 있습니다.

그러나 일반적으로 실행 된 명령이 종료되지 않는다는 사실로 귀결됩니다.

이것 역시 여러 가지 이유가있을 수 있습니다.

한 가지 일반적인 이유는 프로세스가 일부 출력을 생성하고 적절한 스트림에서 읽지 않기 때문입니다. 즉, 버퍼가 가득 차면 프로세스가 차단되고 프로세스가 계속 읽기를 기다립니다. 당신의 프로세스는 다른 프로세스가 끝날 때까지 기다립니다 (당신의 프로세스를 기다리기 때문이 아닙니다 ...). 이것은 고전적인 교착 상태 상황입니다.

차단되지 않도록 프로세스 입력 스트림에서 지속적으로 읽어야합니다.

"When Runtime.exec () wo n't"Runtime.exec() 라는 모든 함정을 설명하고 그에 대한 방법을 보여주는 멋진 기사 가 있습니다.


7
이 대답은 정확하지만 문제를 해결하기위한 코드 샘플이 누락되었습니다. 왜 waitFor()반환되지 않는지 알아 보려면 유용한 코드에 대한 Peter Lawrey 답변을 살펴보십시오 .
ForguesR 2015 년

83

완료되기를 기다리기 전에 출력을 읽지 않는 것 같습니다. 출력이 버퍼를 채우지 않는 경우에만 괜찮습니다. 그렇다면 출력 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();

4
정보 : ProcessBuilder는 실제 빌더이므로 ProcessBuilder를 직접 작성할 수 있습니다. pb = new ProcessBuilder ( "tasklist"). redirectErrorStream (true);
Jean-François Savard 2015

3
차라리 사용하고 싶습니다pb.redirectError(new File("/dev/null"));
Toochka 2015-12-21

@Toochka 그냥 정보, redirectError자바 1.7 이후에서만 사용할 수 있습니다
ZhekaKozlov

3
내가 믿는 대답이되어야한다고 생각하면 코드를 이것으로 바꾸었고 즉시 작동했습니다.
Gerben Rampaart 2017

43

또한 Java 문서에서 :

java.lang

수업 과정

일부 네이티브 플랫폼은 표준 입력 및 출력 스트림에 제한된 버퍼 크기 만 제공하기 때문에 입력 스트림을 즉시 쓰거나 하위 프로세스의 출력 스트림을 읽지 못하면 하위 프로세스가 차단되고 교착 상태가 될 수도 있습니다.

Process에서 입력 스트림의 버퍼 (하위 프로세스의 출력 스트림으로 파이프)를 지우지 못하면 하위 프로세스가 차단 될 수 있습니다.

이 시도:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

17
두 가지주의 사항 : (1) ProcessBuilder + redirectErrorStream (true)을 사용하면 안전합니다. 그렇지 않으면 (2) Process.getInputStream ()에서 읽을 스레드와 Process.getErrorStream ()에서 읽을 스레드가 하나 필요합니다. 이 문제를 해결하는 데 약 4 시간을 보냈습니다 (!), 일명 "하드 웨이".
kevinarpe 2011 년

1
Apache Commons Exec 기능을 사용하여 stdout 및 stderr 스트림을 동시에 사용할 수 있습니다. DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);여기서 stoutOS 및 stderrOS는 BufferedOutputStream적절한 파일에 작성하기 위해 만든 것입니다.
마태 복음 현명한

제 경우에는 내부적으로 하나의 편집기를 여는 Spring에서 배치 파일을 호출했습니다. 코드를 적용한 후에도 내 코드가 중단되었습니다 process.close(). 그러나 위에서 제안한대로 입력 스트림을 열고 즉시 닫으면 문제가 사라집니다. 그래서 제 경우에는 Spring이 스트림 닫기 신호를 기다리고있었습니다. Java 8 auto closable을 사용하고 있지만.
shaILU

11

이전 답변에 추가하고 싶지만 댓글을 달 담당자가 없기 때문에 답변 만 추가하겠습니다. 이것은 Java로 프로그래밍하는 Android 사용자를 대상으로합니다.

RollingBoy의 게시물에 따르면이 코드는 거의 저에게 효과적이었습니다.

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

제 경우에는 return ( "ip adddr flush eth0")없이 명령문을 실행했기 때문에 waitFor ()가 해제되지 않았습니다. 이 문제를 해결하는 쉬운 방법은 단순히 진술에 항상 무언가를 반환하도록하는 것입니다. 저에게 이것은 "ip adddr flush eth0 && echo done"실행을 의미했습니다. 하루 종일 버퍼를 읽을 수 있지만 아무것도 반환되지 않으면 스레드가 대기를 해제하지 않습니다.

누군가에게 도움이되기를 바랍니다!


2
댓글을 달 담당자가없는 경우에는 문제를 해결하고 댓글을 달지 마십시오 . 이것 자체에 대한 답을 만들고 그것으로부터 담당자를 얻으십시오!
기금 모니카의 소송

나는 당신이 출력이 없으면 process.waitFor()매달리는 것이 매달려 있다고 생각하지 않습니다 reader.readLine(). 나는 waitFor(long,TimeUnit)무언가가 잘못되면 to timeout 을 사용하려고 시도했고 그것이 중단 된 읽기임을 발견했습니다. 어느 timeouted 버전은 읽기를 수행하는 다른 스레드를 필요로한다 ...
osundblad

5

몇 가지 가능성이 있습니다.

  1. 당신은 프로세스의 모든 출력을 소비하지 않았습니다 stdout.
  2. 당신은 프로세스의 모든 출력을 소비하지 않았습니다 stderr.
  3. 프로세스가 사용자의 입력 을 기다리고 있으며 제공하지 않았거나 프로세스의 stdin.
  4. 프로세스가 하드 루프로 회전하고 있습니다.

5

다른 사람들이 언급했듯이 stderrstdout 을 사용해야합니다 .

다른 답변과 비교할 때 Java 1.7 이후로 훨씬 쉽습니다. stderrstdout 을 읽기 위해 더 이상 스레드를 직접 만들 필요가 없습니다 .

그냥를 사용 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();

2

같은 이유로 inheritIO()Java 콘솔을 다음과 같은 외부 앱 콘솔과 매핑 하는 데 사용할 수도 있습니다 .

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

pb.directory(new File(appFile.getParent()));
pb.inheritIO();

Process process = pb.start();
int success = process.waitFor();

2

출력과 오류를 동시에 소비해야합니다.

    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");
}

1

비슷한 문제를 목격했다고 생각합니다. 일부 프로세스가 시작되었고 성공적으로 실행되는 것처럼 보였지만 완료되지 않았습니다. 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%

마지막 단계는 런타임을 사용하여이 배치 파일을 실행하는 것입니다.


1

여기 저에게 맞는 방법이 있습니다. 참고 :이 메서드에는 적용되지 않는 코드가 있으므로 무시하십시오. 예 : "logStandardOut (...), git-bash, etc".

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();

}


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.