process.waitFor () asla dönmez


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

Lütfen JAVA 8'de bir zaman aşımı belirtmenize izin veren bir bekleme için aşırı yükleme olduğunu unutmayın. Bu waitFor'un asla geri dönmediği bir durumdan kaçınmak için daha iyi bir seçim olabilir.
Ikaso

Yanıtlar:


146

waitFor()Geri dönmeyen birçok neden var .

Ancak genellikle yürütülen komutun çıkmaması gerçeğine dayanır.

Bunun da birçok nedeni olabilir.

Yaygın bir neden, işlemin bir miktar çıktı üretmesi ve uygun akışlardan okumamanızdır. Bu, arabellek dolduğu anda işlemin engelleneceği ve işleminizin okumaya devam etmesini beklediği anlamına gelir. İşleminiz sırayla diğer işlemin bitmesini bekler (ki bu işleminizi beklediği için bitmez ...). Bu klasik bir kilitlenme durumudur.

Engellemediğinden emin olmak için işlem giriş akışını sürekli olarak okumanız gerekir.

Tüm tuzaklarını açıklayan güzel bir makale var Runtime.exec()etraflarında ve gösterileri yolları denilen "Ne zaman Runtime.exec () olmaz" (evet, makale 2000 olmakla birlikte, içerik yine geçerlidir!)


8
Bu cevap doğrudur, ancak sorunu gidermek için bir kod örneğini kaçırmaktadır. Neden waitFor()geri dönmediğini öğrenmek için yararlı kod için Peter Lawrey yanıtına bir göz atın .
ForguesR

84

Görünüşe göre, bitmesini beklemeden çıktıyı okumuyorsunuz. Bu sadece çıktı arabelleği doldurmuyorsa iyidir. Eğer öyleyse, çıktıyı, catch-22'yi okuyana kadar bekler.

Belki de okumadığınız bazı hatalarınız vardır. Bu, uygulamanın durması ve sonsuza kadar beklemesi anlamına gelir. Bunu aşmanın basit bir yolu, hataları normal çıktıya yeniden yönlendirmektir.

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
Bilgi için: ProcessBuilder gerçek bir kurucu olduğundan, ProcessBuilder pb = new ProcessBuilder ("tasklist") yazabilirsiniz. RedirectErrorStream (true);
Jean-François Savard

3
pb.redirectError(new File("/dev/null"));
Kullanmayı

@Toochka Sadece bilgi amaçlı, redirectErroryalnızca Java 1.7'den beri mevcuttur
ZhekaKozlov

3
Kabul edilen cevap olmalı inanıyorum, kodumu bununla değiştirdim ve hemen çalıştı.
Gerben Rampaart

43

Ayrıca Java belgesinden:

java.lang

Sınıf Süreci

Bazı yerel platformlar, standart giriş ve çıkış akışları için yalnızca sınırlı arabellek boyutu sağladığından, alt işlemin giriş akışının hemen yazılmaması veya çıkış akışının okunmaması, alt işlemin engellenmesine ve hatta kilitlenmesine neden olabilir.

Giriş akışı tamponunun (alt işlemin çıkış akışına giden) İşlemden temizlenememesi, bir alt işlemin bloke olmasına neden olabilir.

Bunu dene:

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

17
İki uyarı: (1) ProcessBuilder + redirectErrorStream (true) kullanın, o zaman güvendesiniz. Aksi takdirde, (2) Process.getInputStream () 'den okumak için bir iş parçacığına ve Process.getErrorStream ()' den okumak için başka bir iş parçacığına ihtiyacınız vardır. Bunu anlamak için yaklaşık dört saat harcadım (!), Aka, "Zor Yol".
kevinarpe

1
DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);Stdout ve stderr akışlarını aynı anda tüketmek için Apache Commons Exec işlevini kullanabilirsiniz: burada stoutOS ve stderrOS, BufferedOutputStreamuygun dosyalara yazmak için oluşturduğum s.
Matthew Wise

Benim durumumda, Spring'ten dahili olarak bir düzenleyici açan bir toplu iş dosyası çağırıyordum. Kodunu uyguladıktan sonra bile kodum takılıyordu process.close(). Ancak yukarıda önerildiği gibi giriş akışını açıp hemen kapattığımda sorun ortadan kalkıyor. Benim durumumda Bahar, akışın kapanma sinyalini bekliyordu. Java 8 otomatik kapatılabilir kullanmama rağmen.
shaILU

11

Önceki cevaplara bir şeyler eklemek istiyorum ancak yorum yapacak temsilcim olmadığı için sadece bir cevap ekleyeceğim. Bu, Java'da programlama yapan android kullanıcılarına yöneliktir.

RollingBoy'daki gönderiye göre, bu kod neredeyse benim için çalıştı:

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

Benim durumumda, waitFor () yayınlanmıyordu çünkü dönüşü olmayan bir ifade ("ip adddr flush eth0") yürütüyordum. Bunu düzeltmenin kolay bir yolu, ifadenizde her zaman bir şey döndürmenizi sağlamaktır. Benim için bunun anlamı şuydu: "ip adddr flush eth0 && echo done". Tüm gün tamponu okuyabilirsiniz, ancak geri dönen hiçbir şey yoksa, iş parçacığınız asla beklemesini bırakmaz.

Umarım bu birine yardımcı olur!


2
Yorum yapacak temsilciniz olmadığında, bunun üzerinde çalışmayın ve yorum yapmayın . Bunu kendi başına bir cevap haline getirin ve ondan itibar alın!
Fund Monica'nın Davası

Ben öyle düşünmüyorum process.waitFor()öyle takılırsa o reader.readLine()hiçbir çıkış varsa o takılıyor. Bir waitFor(long,TimeUnit)şeyler ters giderse zaman aşımını kullanmaya çalıştım ve bunun asılı kaldığını keşfettim. Hangi timeouted versiyonu okuma yapmak başka iş parçacığı gerektiren yapar ...
osundblad

5

Birkaç olasılık var:

  1. Sürecin tüm çıktılarını tüketmediniz stdout.
  2. Sürecin tüm çıktılarını tüketmediniz stderr.
  3. İşlem sizden girdi bekliyor ve siz sağlamadınız veya işlemi kapatmadınız stdin.
  4. İşlem sert bir döngü içinde dönüyor.

5

Başkalarının da bahsettiği gibi, stderr ve stdout tüketmelisiniz .

Diğer cevaplarla karşılaştırıldığında, Java 1.7'den beri daha da kolaydır. Artık stderr ve stdout okumak için kendi başınıza iş parçacıkları oluşturmanız gerekmiyor .

Sadece kullanın ProcessBuilderve yöntemleri veya redirectOutputile kombinasyon halinde kullanın .redirectErrorredirectErrorStream

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

Aynı nedenle, inheritIO()Java konsolunu aşağıdaki gibi harici uygulama konsoluyla eşlemek için de kullanabilirsiniz :

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

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

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

2

Çıktı ve hatayı aynı anda tüketmeyi denemelisiniz

    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

Sanırım benzer bir sorun gözlemledim: bazı işlemler başladı, başarılı bir şekilde çalışıyor gibiydi ama hiç tamamlanmadı. WaitFor () işlevi, Görev Yöneticisi'ndeki işlemi sonlandırmam dışında sonsuza kadar bekliyordu.
Bununla birlikte, komut satırı uzunluğunun 127 karakter veya daha kısa olduğu durumlarda her şey yolunda gitti. Uzun dosya adları kaçınılmazsa, komut satırı dizesini kısa tutmanıza izin veren çevresel değişkenleri kullanmak isteyebilirsiniz. Gerçekten çalıştırmak istediğiniz programı çağırmadan önce çevresel değişkenlerinizi ayarladığınız bir toplu iş dosyası (FileWriter kullanarak) oluşturabilirsiniz. Böyle bir partinin içeriği şöyle görünebilir:

    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%

Son adım, bu toplu iş dosyasını Runtime kullanarak çalıştırmaktır.


1

İşte benim için çalışan bir yöntem. NOT: Bu yöntemde sizin için geçerli olmayabilecek bazı kodlar vardır, bu yüzden deneyin ve görmezden gelin. Örneğin "logStandardOut (...), git-bash, vb."

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

}


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.