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();
回答:
それには多くの理由があります waitFor()
戻らない。
しかし、それは通常、実行されたコマンドが終了しないという事実に要約されます。
これも、多くの理由が考えられます。
一般的な理由の1つは、プロセスが出力を生成し、適切なストリームから読み取らないことです。つまり、バッファがいっぱいになるとすぐにプロセスがブロックされ、プロセスが読み取りを続行するのを待ちます。次に、プロセスは他のプロセスが完了するのを待ちます(プロセスを待つので、そうなりません...)。これは古典的なデッドロック状況です。
プロセスの入力ストリームから継続的に読み取り、ブロックされないようにする必要があります。
「Runtime.exec()が実行されない場合」Runtime.exec()
と呼ばれるすべての落とし穴を説明し、その回避策を示す素晴らしい記事があります(そうです、記事は2000年のものですが、コンテンツは引き続き適用されます!)
waitFor()
戻らないかを調べてください。
出力が完了するのを待つ前に、出力を読み取っていないようです。これは、出力がバッファを満たさない場合にのみ問題ありません。存在する場合は、出力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();
pb.redirectError(new File("/dev/null"));
redirectError
、Java 1.7以降でのみ利用可能
またJavaドキュメントから:
java.lang
クラスプロセス
一部のネイティブプラットフォームは標準の入力および出力ストリームに限られたバッファーサイズしか提供しないため、サブプロセスの入力ストリームの書き込みまたは出力ストリームの読み取りに失敗すると、サブプロセスがブロックされ、さらにはデッドロックが発生する可能性があります。
プロセスから入力ストリーム(サブプロセスの出力ストリームにパイプする)のバッファーをクリアしないと、サブプロセスがブロックされる可能性があります。
これを試して:
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);
。ここで、stoutOSとstderrOSはBufferedOutputStream
、適切なファイルに書き出すために作成したものです。
process.close()
。しかし、上記のように入力ストリームを開いてすぐに閉じると、問題はなくなります。私の場合、Springはストリームのクローズ信号を待っていました。私はJava 8 auto closableを使用していますが。
以前の回答に何かを追加したいのですが、コメントする担当者がいないため、回答を追加します。これは、JavaでプログラミングしているAndroidユーザーを対象としています。
RollingBoyからの投稿によると、このコードはほとんど私にとってうまくいきました:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
私の場合、リターンなしのステートメント( "ip adddr flush eth0")を実行していたため、waitFor()は解放されませんでした。これを修正する簡単な方法は、常にステートメントで何かを返すようにすることです。私にとって、それは次を実行することを意味しました: "ip adddr flush eth0 && echo done"。バッファは1日中読み取ることができますが、何も返されない場合、スレッドは待機を解放しません。
それが誰かを助けることを願っています!
process.waitFor()
ハングするのはハングアップだとは思いませんreader.readLine()
。私はwaitFor(long,TimeUnit)
何かがうまくいかない場合にタイムアウトを使用してみましたが、それがハングの読み取りであることがわかりました。これにより、タイムアウトバージョンでは読み取りを実行するために別のスレッドが必要になります...
他の人が述べたように、あなたは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();
あなたは同時に出力とエラーを消費してみてください
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");
}
私は同様の問題を観察したと思います:いくつかのプロセスは開始しましたが、正常に実行されているように見えますが、完了していません。関数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%
最後のステップは、ランタイムを使用してこのバッチファイルを実行することです。
ここに私のために働く方法があります。注:このメソッドには適用されない可能性のあるコードがいくつかあるため、無視してください。たとえば、「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
// /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();
}
タイムアウト付きの待機を回避することと組み合わせたストリームの非同期読み取りは、問題を解決します。
これを説明しているページはこちらhttp://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/