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()
无法返回的原因有很多。
但通常归结为执行的命令没有退出。
这又可以有很多原因。
其中一个常见的原因是该进程生成了一些输出,而您没有从适当的流中读取。这意味着一旦缓冲区已满,进程就会被阻塞,并等待您的进程继续读取。您的进程反过来又等待另一个进程完成(但它不会完成,因为它在等待您的进程...)。这是一种经典的死锁情况。
您需要不断地从进程的输入流中读取内容,以确保其不会被阻塞。
有一篇很好的文章详细解释了Runtime.exec()
存在的所有陷阱,并展示了绕过它们的方法,名为"When Runtime.exec() won't"(是的,这篇文章发表于2000年,但内容仍然适用!)
waitFor()
没有返回。 - ForguesRls
它会退出,但如果你启动 httpd
呢? - d-bwaitFor()
不返回的另一个原因。 - Joachim Sauer看起来您在等待输出完成之前没有读取它。只有当输出不填满缓冲区时,这种情况才可以接受。但如果输出填满了缓冲区,它将等待您读取输出,这是一个进退两难的局面。
也许您有一些错误未被读取。这会导致应用程序停止并使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"));
。 - ToochkaredirectError
仅在 Java 1.7 及以上版本可用。 - ZhekaKozlovwaitFor
? - Sybuser来自Java文档:
java.lang
类 Process
因为一些原生平台只为标准输入和输出流提供有限的缓冲区大小,不能及时地写入子进程的输入流或读取子进程的输出流可能会导致子进程阻塞,甚至死锁。
如果未清除从进程到输出流的输入流(它是子进程的输出流)的缓冲区,则可能导致子进程阻塞。
试试这个:
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
,用于将输出写入相应的文件。 - Matthew Wiseprocess.close()
的代码,我的代码仍然挂起。但是当我像上面建议的那样打开输入流并立即关闭时,问题就解决了。因此,在我的情况下,Spring正在等待流关闭信号。尽管我正在使用Java 8自动可关闭功能。 - shaILUInputStream.available()
方法在单个线程中消耗两个流而不会阻塞。只要该方法返回一个正数,就保证读取操作不会被阻塞。 - Gili我想在之前的答案中补充一些内容,但由于我没有足够的声望来发表评论,所以我只能添加一个回答。这是针对在Java编程的安卓用户。
根据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"。你可以整天读取缓冲区,但如果没有任何返回值,你的线程将永远不会释放它的等待。
希望能对某人有所帮助!
process.waitFor()
,而是如果没有输出,则会挂起 reader.readLine()
。我尝试使用 waitFor(long, TimeUnit)
设置超时时间,以防出现问题,并发现是读取操作挂起了。这使得设置了超时的版本需要另一个线程来执行读取操作... - osundblad有几种可能性:
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();
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");
}
inheritIO()
将Java控制台映射到外部应用程序控制台,例如:ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
这是我使用的一种方法。 注意:此方法中可能有些代码不适用于您,请尝试忽略它。例如"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://dev59.com/r2035IYBdhLWcg3wVud1
//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运行此批处理文件。
if(!process.waitFor(15, TimeUnit.MINUTES)) { process.destroy(); } else { process.getOutputStream().close(); BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
- SRJ